5f3b13414b08901f3b03d902507ec780be719925
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = []; //config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         this.addColumn(config[i]);
7316         
7317     }
7318
7319     /**
7320      * The width of columns which have no width specified (defaults to 100)
7321      * @type Number
7322      */
7323     this.defaultWidth = 100;
7324
7325     /**
7326      * Default sortable of columns which have no sortable specified (defaults to false)
7327      * @type Boolean
7328      */
7329     this.defaultSortable = false;
7330
7331     this.addEvents({
7332         /**
7333              * @event widthchange
7334              * Fires when the width of a column changes.
7335              * @param {ColumnModel} this
7336              * @param {Number} columnIndex The column index
7337              * @param {Number} newWidth The new width
7338              */
7339             "widthchange": true,
7340         /**
7341              * @event headerchange
7342              * Fires when the text of a header changes.
7343              * @param {ColumnModel} this
7344              * @param {Number} columnIndex The column index
7345              * @param {Number} newText The new header text
7346              */
7347             "headerchange": true,
7348         /**
7349              * @event hiddenchange
7350              * Fires when a column is hidden or "unhidden".
7351              * @param {ColumnModel} this
7352              * @param {Number} columnIndex The column index
7353              * @param {Boolean} hidden true if hidden, false otherwise
7354              */
7355             "hiddenchange": true,
7356             /**
7357          * @event columnmoved
7358          * Fires when a column is moved.
7359          * @param {ColumnModel} this
7360          * @param {Number} oldIndex
7361          * @param {Number} newIndex
7362          */
7363         "columnmoved" : true,
7364         /**
7365          * @event columlockchange
7366          * Fires when a column's locked state is changed
7367          * @param {ColumnModel} this
7368          * @param {Number} colIndex
7369          * @param {Boolean} locked true if locked
7370          */
7371         "columnlockchange" : true
7372     });
7373     Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 };
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376     /**
7377      * @cfg {String} header The header text to display in the Grid view.
7378      */
7379     /**
7380      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382      * specified, the column's index is used as an index into the Record's data Array.
7383      */
7384     /**
7385      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7387      */
7388     /**
7389      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390      * Defaults to the value of the {@link #defaultSortable} property.
7391      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7392      */
7393     /**
7394      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7395      */
7396     /**
7397      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7398      */
7399     /**
7400      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7401      */
7402     /**
7403      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7404      */
7405     /**
7406      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7410      */
7411        /**
7412      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7413      */
7414     /**
7415      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7416      */
7417     /**
7418      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7419      */
7420     /**
7421      * @cfg {String} cursor (Optional)
7422      */
7423     /**
7424      * @cfg {String} tooltip (Optional)
7425      */
7426     /**
7427      * @cfg {Number} xs (Optional)
7428      */
7429     /**
7430      * @cfg {Number} sm (Optional)
7431      */
7432     /**
7433      * @cfg {Number} md (Optional)
7434      */
7435     /**
7436      * @cfg {Number} lg (Optional)
7437      */
7438     /**
7439      * Returns the id of the column at the specified index.
7440      * @param {Number} index The column index
7441      * @return {String} the id
7442      */
7443     getColumnId : function(index){
7444         return this.config[index].id;
7445     },
7446
7447     /**
7448      * Returns the column for a specified id.
7449      * @param {String} id The column id
7450      * @return {Object} the column
7451      */
7452     getColumnById : function(id){
7453         return this.lookup[id];
7454     },
7455
7456     
7457     /**
7458      * Returns the column for a specified dataIndex.
7459      * @param {String} dataIndex The column dataIndex
7460      * @return {Object|Boolean} the column or false if not found
7461      */
7462     getColumnByDataIndex: function(dataIndex){
7463         var index = this.findColumnIndex(dataIndex);
7464         return index > -1 ? this.config[index] : false;
7465     },
7466     
7467     /**
7468      * Returns the index for a specified column id.
7469      * @param {String} id The column id
7470      * @return {Number} the index, or -1 if not found
7471      */
7472     getIndexById : function(id){
7473         for(var i = 0, len = this.config.length; i < len; i++){
7474             if(this.config[i].id == id){
7475                 return i;
7476             }
7477         }
7478         return -1;
7479     },
7480     
7481     /**
7482      * Returns the index for a specified column dataIndex.
7483      * @param {String} dataIndex The column dataIndex
7484      * @return {Number} the index, or -1 if not found
7485      */
7486     
7487     findColumnIndex : function(dataIndex){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].dataIndex == dataIndex){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     
7497     moveColumn : function(oldIndex, newIndex){
7498         var c = this.config[oldIndex];
7499         this.config.splice(oldIndex, 1);
7500         this.config.splice(newIndex, 0, c);
7501         this.dataMap = null;
7502         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7503     },
7504
7505     isLocked : function(colIndex){
7506         return this.config[colIndex].locked === true;
7507     },
7508
7509     setLocked : function(colIndex, value, suppressEvent){
7510         if(this.isLocked(colIndex) == value){
7511             return;
7512         }
7513         this.config[colIndex].locked = value;
7514         if(!suppressEvent){
7515             this.fireEvent("columnlockchange", this, colIndex, value);
7516         }
7517     },
7518
7519     getTotalLockedWidth : function(){
7520         var totalWidth = 0;
7521         for(var i = 0; i < this.config.length; i++){
7522             if(this.isLocked(i) && !this.isHidden(i)){
7523                 this.totalWidth += this.getColumnWidth(i);
7524             }
7525         }
7526         return totalWidth;
7527     },
7528
7529     getLockedCount : function(){
7530         for(var i = 0, len = this.config.length; i < len; i++){
7531             if(!this.isLocked(i)){
7532                 return i;
7533             }
7534         }
7535         
7536         return this.config.length;
7537     },
7538
7539     /**
7540      * Returns the number of columns.
7541      * @return {Number}
7542      */
7543     getColumnCount : function(visibleOnly){
7544         if(visibleOnly === true){
7545             var c = 0;
7546             for(var i = 0, len = this.config.length; i < len; i++){
7547                 if(!this.isHidden(i)){
7548                     c++;
7549                 }
7550             }
7551             return c;
7552         }
7553         return this.config.length;
7554     },
7555
7556     /**
7557      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558      * @param {Function} fn
7559      * @param {Object} scope (optional)
7560      * @return {Array} result
7561      */
7562     getColumnsBy : function(fn, scope){
7563         var r = [];
7564         for(var i = 0, len = this.config.length; i < len; i++){
7565             var c = this.config[i];
7566             if(fn.call(scope||this, c, i) === true){
7567                 r[r.length] = c;
7568             }
7569         }
7570         return r;
7571     },
7572
7573     /**
7574      * Returns true if the specified column is sortable.
7575      * @param {Number} col The column index
7576      * @return {Boolean}
7577      */
7578     isSortable : function(col){
7579         if(typeof this.config[col].sortable == "undefined"){
7580             return this.defaultSortable;
7581         }
7582         return this.config[col].sortable;
7583     },
7584
7585     /**
7586      * Returns the rendering (formatting) function defined for the column.
7587      * @param {Number} col The column index.
7588      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589      */
7590     getRenderer : function(col){
7591         if(!this.config[col].renderer){
7592             return Roo.grid.ColumnModel.defaultRenderer;
7593         }
7594         return this.config[col].renderer;
7595     },
7596
7597     /**
7598      * Sets the rendering (formatting) function for a column.
7599      * @param {Number} col The column index
7600      * @param {Function} fn The function to use to process the cell's raw data
7601      * to return HTML markup for the grid view. The render function is called with
7602      * the following parameters:<ul>
7603      * <li>Data value.</li>
7604      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605      * <li>css A CSS style string to apply to the table cell.</li>
7606      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608      * <li>Row index</li>
7609      * <li>Column index</li>
7610      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611      */
7612     setRenderer : function(col, fn){
7613         this.config[col].renderer = fn;
7614     },
7615
7616     /**
7617      * Returns the width for the specified column.
7618      * @param {Number} col The column index
7619      * @return {Number}
7620      */
7621     getColumnWidth : function(col){
7622         return this.config[col].width * 1 || this.defaultWidth;
7623     },
7624
7625     /**
7626      * Sets the width for a column.
7627      * @param {Number} col The column index
7628      * @param {Number} width The new width
7629      */
7630     setColumnWidth : function(col, width, suppressEvent){
7631         this.config[col].width = width;
7632         this.totalWidth = null;
7633         if(!suppressEvent){
7634              this.fireEvent("widthchange", this, col, width);
7635         }
7636     },
7637
7638     /**
7639      * Returns the total width of all columns.
7640      * @param {Boolean} includeHidden True to include hidden column widths
7641      * @return {Number}
7642      */
7643     getTotalWidth : function(includeHidden){
7644         if(!this.totalWidth){
7645             this.totalWidth = 0;
7646             for(var i = 0, len = this.config.length; i < len; i++){
7647                 if(includeHidden || !this.isHidden(i)){
7648                     this.totalWidth += this.getColumnWidth(i);
7649                 }
7650             }
7651         }
7652         return this.totalWidth;
7653     },
7654
7655     /**
7656      * Returns the header for the specified column.
7657      * @param {Number} col The column index
7658      * @return {String}
7659      */
7660     getColumnHeader : function(col){
7661         return this.config[col].header;
7662     },
7663
7664     /**
7665      * Sets the header for a column.
7666      * @param {Number} col The column index
7667      * @param {String} header The new header
7668      */
7669     setColumnHeader : function(col, header){
7670         this.config[col].header = header;
7671         this.fireEvent("headerchange", this, col, header);
7672     },
7673
7674     /**
7675      * Returns the tooltip for the specified column.
7676      * @param {Number} col The column index
7677      * @return {String}
7678      */
7679     getColumnTooltip : function(col){
7680             return this.config[col].tooltip;
7681     },
7682     /**
7683      * Sets the tooltip for a column.
7684      * @param {Number} col The column index
7685      * @param {String} tooltip The new tooltip
7686      */
7687     setColumnTooltip : function(col, tooltip){
7688             this.config[col].tooltip = tooltip;
7689     },
7690
7691     /**
7692      * Returns the dataIndex for the specified column.
7693      * @param {Number} col The column index
7694      * @return {Number}
7695      */
7696     getDataIndex : function(col){
7697         return this.config[col].dataIndex;
7698     },
7699
7700     /**
7701      * Sets the dataIndex for a column.
7702      * @param {Number} col The column index
7703      * @param {Number} dataIndex The new dataIndex
7704      */
7705     setDataIndex : function(col, dataIndex){
7706         this.config[col].dataIndex = dataIndex;
7707     },
7708
7709     
7710     
7711     /**
7712      * Returns true if the cell is editable.
7713      * @param {Number} colIndex The column index
7714      * @param {Number} rowIndex The row index - this is nto actually used..?
7715      * @return {Boolean}
7716      */
7717     isCellEditable : function(colIndex, rowIndex){
7718         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7719     },
7720
7721     /**
7722      * Returns the editor defined for the cell/column.
7723      * return false or null to disable editing.
7724      * @param {Number} colIndex The column index
7725      * @param {Number} rowIndex The row index
7726      * @return {Object}
7727      */
7728     getCellEditor : function(colIndex, rowIndex){
7729         return this.config[colIndex].editor;
7730     },
7731
7732     /**
7733      * Sets if a column is editable.
7734      * @param {Number} col The column index
7735      * @param {Boolean} editable True if the column is editable
7736      */
7737     setEditable : function(col, editable){
7738         this.config[col].editable = editable;
7739     },
7740
7741
7742     /**
7743      * Returns true if the column is hidden.
7744      * @param {Number} colIndex The column index
7745      * @return {Boolean}
7746      */
7747     isHidden : function(colIndex){
7748         return this.config[colIndex].hidden;
7749     },
7750
7751
7752     /**
7753      * Returns true if the column width cannot be changed
7754      */
7755     isFixed : function(colIndex){
7756         return this.config[colIndex].fixed;
7757     },
7758
7759     /**
7760      * Returns true if the column can be resized
7761      * @return {Boolean}
7762      */
7763     isResizable : function(colIndex){
7764         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7765     },
7766     /**
7767      * Sets if a column is hidden.
7768      * @param {Number} colIndex The column index
7769      * @param {Boolean} hidden True if the column is hidden
7770      */
7771     setHidden : function(colIndex, hidden){
7772         this.config[colIndex].hidden = hidden;
7773         this.totalWidth = null;
7774         this.fireEvent("hiddenchange", this, colIndex, hidden);
7775     },
7776
7777     /**
7778      * Sets the editor for a column.
7779      * @param {Number} col The column index
7780      * @param {Object} editor The editor object
7781      */
7782     setEditor : function(col, editor){
7783         this.config[col].editor = editor;
7784     },
7785     /**
7786      * Add a column (experimental...) - defaults to adding to the end..
7787      * @param {Object} config 
7788     */
7789     addColumn : function(c)
7790     {
7791     
7792         var i = this.config.length;
7793         this.config[i] = c;
7794         
7795         if(typeof c.dataIndex == "undefined"){
7796             c.dataIndex = i;
7797         }
7798         if(typeof c.renderer == "string"){
7799             c.renderer = Roo.util.Format[c.renderer];
7800         }
7801         if(typeof c.id == "undefined"){
7802             c.id = Roo.id();
7803         }
7804         if(c.editor && c.editor.xtype){
7805             c.editor  = Roo.factory(c.editor, Roo.grid);
7806         }
7807         if(c.editor && c.editor.isFormField){
7808             c.editor = new Roo.grid.GridEditor(c.editor);
7809         }
7810         this.lookup[c.id] = c;
7811     }
7812     
7813 });
7814
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7816 {
7817     if(typeof value == "object") {
7818         return value;
7819     }
7820         if(typeof value == "string" && value.length < 1){
7821             return "&#160;";
7822         }
7823     
7824         return String.format("{0}", value);
7825 };
7826
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7829 /*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839  
7840 /**
7841  * @class Roo.LoadMask
7842  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7843  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7845  * element's UpdateManager load indicator and will be destroyed after the initial load.
7846  * @constructor
7847  * Create a new LoadMask
7848  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849  * @param {Object} config The config object
7850  */
7851 Roo.LoadMask = function(el, config){
7852     this.el = Roo.get(el);
7853     Roo.apply(this, config);
7854     if(this.store){
7855         this.store.on('beforeload', this.onBeforeLoad, this);
7856         this.store.on('load', this.onLoad, this);
7857         this.store.on('loadexception', this.onLoadException, this);
7858         this.removeMask = false;
7859     }else{
7860         var um = this.el.getUpdateManager();
7861         um.showLoadIndicator = false; // disable the default indicator
7862         um.on('beforeupdate', this.onBeforeLoad, this);
7863         um.on('update', this.onLoad, this);
7864         um.on('failure', this.onLoad, this);
7865         this.removeMask = true;
7866     }
7867 };
7868
7869 Roo.LoadMask.prototype = {
7870     /**
7871      * @cfg {Boolean} removeMask
7872      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7874      */
7875     /**
7876      * @cfg {String} msg
7877      * The text to display in a centered loading message box (defaults to 'Loading...')
7878      */
7879     msg : 'Loading...',
7880     /**
7881      * @cfg {String} msgCls
7882      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7883      */
7884     msgCls : 'x-mask-loading',
7885
7886     /**
7887      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7888      * @type Boolean
7889      */
7890     disabled: false,
7891
7892     /**
7893      * Disables the mask to prevent it from being displayed
7894      */
7895     disable : function(){
7896        this.disabled = true;
7897     },
7898
7899     /**
7900      * Enables the mask so that it can be displayed
7901      */
7902     enable : function(){
7903         this.disabled = false;
7904     },
7905     
7906     onLoadException : function()
7907     {
7908         Roo.log(arguments);
7909         
7910         if (typeof(arguments[3]) != 'undefined') {
7911             Roo.MessageBox.alert("Error loading",arguments[3]);
7912         } 
7913         /*
7914         try {
7915             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7917             }   
7918         } catch(e) {
7919             
7920         }
7921         */
7922     
7923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7924     },
7925     // private
7926     onLoad : function()
7927     {
7928         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7929     },
7930
7931     // private
7932     onBeforeLoad : function(){
7933         if(!this.disabled){
7934             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7935         }
7936     },
7937
7938     // private
7939     destroy : function(){
7940         if(this.store){
7941             this.store.un('beforeload', this.onBeforeLoad, this);
7942             this.store.un('load', this.onLoad, this);
7943             this.store.un('loadexception', this.onLoadException, this);
7944         }else{
7945             var um = this.el.getUpdateManager();
7946             um.un('beforeupdate', this.onBeforeLoad, this);
7947             um.un('update', this.onLoad, this);
7948             um.un('failure', this.onLoad, this);
7949         }
7950     }
7951 };/*
7952  * - LGPL
7953  *
7954  * table
7955  * 
7956  */
7957
7958 /**
7959  * @class Roo.bootstrap.Table
7960  * @extends Roo.bootstrap.Component
7961  * Bootstrap Table class
7962  * @cfg {String} cls table class
7963  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964  * @cfg {String} bgcolor Specifies the background color for a table
7965  * @cfg {Number} border Specifies whether the table cells should have borders or not
7966  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967  * @cfg {Number} cellspacing Specifies the space between cells
7968  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970  * @cfg {String} sortable Specifies that the table should be sortable
7971  * @cfg {String} summary Specifies a summary of the content of a table
7972  * @cfg {Number} width Specifies the width of a table
7973  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7974  * 
7975  * @cfg {boolean} striped Should the rows be alternative striped
7976  * @cfg {boolean} bordered Add borders to the table
7977  * @cfg {boolean} hover Add hover highlighting
7978  * @cfg {boolean} condensed Format condensed
7979  * @cfg {boolean} responsive Format condensed
7980  * @cfg {Boolean} loadMask (true|false) default false
7981  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983  * @cfg {Boolean} rowSelection (true|false) default false
7984  * @cfg {Boolean} cellSelection (true|false) default false
7985  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7987  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7988  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7989  
7990  * 
7991  * @constructor
7992  * Create a new Table
7993  * @param {Object} config The config object
7994  */
7995
7996 Roo.bootstrap.Table = function(config){
7997     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7998     
7999   
8000     
8001     // BC...
8002     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8006     
8007     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8008     if (this.sm) {
8009         this.sm.grid = this;
8010         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011         this.sm = this.selModel;
8012         this.sm.xmodule = this.xmodule || false;
8013     }
8014     
8015     if (this.cm && typeof(this.cm.config) == 'undefined') {
8016         this.colModel = new Roo.grid.ColumnModel(this.cm);
8017         this.cm = this.colModel;
8018         this.cm.xmodule = this.xmodule || false;
8019     }
8020     if (this.store) {
8021         this.store= Roo.factory(this.store, Roo.data);
8022         this.ds = this.store;
8023         this.ds.xmodule = this.xmodule || false;
8024          
8025     }
8026     if (this.footer && this.store) {
8027         this.footer.dataSource = this.ds;
8028         this.footer = Roo.factory(this.footer);
8029     }
8030     
8031     /** @private */
8032     this.addEvents({
8033         /**
8034          * @event cellclick
8035          * Fires when a cell is clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Number} columnIndex
8040          * @param {Roo.EventObject} e
8041          */
8042         "cellclick" : true,
8043         /**
8044          * @event celldblclick
8045          * Fires when a cell is double clicked
8046          * @param {Roo.bootstrap.Table} this
8047          * @param {Roo.Element} el
8048          * @param {Number} rowIndex
8049          * @param {Number} columnIndex
8050          * @param {Roo.EventObject} e
8051          */
8052         "celldblclick" : true,
8053         /**
8054          * @event rowclick
8055          * Fires when a row is clicked
8056          * @param {Roo.bootstrap.Table} this
8057          * @param {Roo.Element} el
8058          * @param {Number} rowIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "rowclick" : true,
8062         /**
8063          * @event rowdblclick
8064          * Fires when a row is double clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Roo.Element} el
8067          * @param {Number} rowIndex
8068          * @param {Roo.EventObject} e
8069          */
8070         "rowdblclick" : true,
8071         /**
8072          * @event mouseover
8073          * Fires when a mouseover occur
8074          * @param {Roo.bootstrap.Table} this
8075          * @param {Roo.Element} el
8076          * @param {Number} rowIndex
8077          * @param {Number} columnIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "mouseover" : true,
8081         /**
8082          * @event mouseout
8083          * Fires when a mouseout occur
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Roo.Element} el
8086          * @param {Number} rowIndex
8087          * @param {Number} columnIndex
8088          * @param {Roo.EventObject} e
8089          */
8090         "mouseout" : true,
8091         /**
8092          * @event rowclass
8093          * Fires when a row is rendered, so you can change add a style to it.
8094          * @param {Roo.bootstrap.Table} this
8095          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8096          */
8097         'rowclass' : true,
8098           /**
8099          * @event rowsrendered
8100          * Fires when all the  rows have been rendered
8101          * @param {Roo.bootstrap.Table} this
8102          */
8103         'rowsrendered' : true,
8104         /**
8105          * @event contextmenu
8106          * The raw contextmenu event for the entire grid.
8107          * @param {Roo.EventObject} e
8108          */
8109         "contextmenu" : true,
8110         /**
8111          * @event rowcontextmenu
8112          * Fires when a row is right clicked
8113          * @param {Roo.bootstrap.Table} this
8114          * @param {Number} rowIndex
8115          * @param {Roo.EventObject} e
8116          */
8117         "rowcontextmenu" : true,
8118         /**
8119          * @event cellcontextmenu
8120          * Fires when a cell is right clicked
8121          * @param {Roo.bootstrap.Table} this
8122          * @param {Number} rowIndex
8123          * @param {Number} cellIndex
8124          * @param {Roo.EventObject} e
8125          */
8126          "cellcontextmenu" : true,
8127          /**
8128          * @event headercontextmenu
8129          * Fires when a header is right clicked
8130          * @param {Roo.bootstrap.Table} this
8131          * @param {Number} columnIndex
8132          * @param {Roo.EventObject} e
8133          */
8134         "headercontextmenu" : true
8135     });
8136 };
8137
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8139     
8140     cls: false,
8141     align: false,
8142     bgcolor: false,
8143     border: false,
8144     cellpadding: false,
8145     cellspacing: false,
8146     frame: false,
8147     rules: false,
8148     sortable: false,
8149     summary: false,
8150     width: false,
8151     striped : false,
8152     scrollBody : false,
8153     bordered: false,
8154     hover:  false,
8155     condensed : false,
8156     responsive : false,
8157     sm : false,
8158     cm : false,
8159     store : false,
8160     loadMask : false,
8161     footerShow : true,
8162     headerShow : true,
8163   
8164     rowSelection : false,
8165     cellSelection : false,
8166     layout : false,
8167     
8168     // Roo.Element - the tbody
8169     mainBody: false,
8170     // Roo.Element - thead element
8171     mainHead: false,
8172     
8173     container: false, // used by gridpanel...
8174     
8175     lazyLoad : false,
8176     
8177     CSS : Roo.util.CSS,
8178     
8179     auto_hide_footer : false,
8180     
8181     getAutoCreate : function()
8182     {
8183         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8184         
8185         cfg = {
8186             tag: 'table',
8187             cls : 'table',
8188             cn : []
8189         };
8190         if (this.scrollBody) {
8191             cfg.cls += ' table-body-fixed';
8192         }    
8193         if (this.striped) {
8194             cfg.cls += ' table-striped';
8195         }
8196         
8197         if (this.hover) {
8198             cfg.cls += ' table-hover';
8199         }
8200         if (this.bordered) {
8201             cfg.cls += ' table-bordered';
8202         }
8203         if (this.condensed) {
8204             cfg.cls += ' table-condensed';
8205         }
8206         if (this.responsive) {
8207             cfg.cls += ' table-responsive';
8208         }
8209         
8210         if (this.cls) {
8211             cfg.cls+=  ' ' +this.cls;
8212         }
8213         
8214         // this lot should be simplifed...
8215         var _t = this;
8216         var cp = [
8217             'align',
8218             'bgcolor',
8219             'border',
8220             'cellpadding',
8221             'cellspacing',
8222             'frame',
8223             'rules',
8224             'sortable',
8225             'summary',
8226             'width'
8227         ].forEach(function(k) {
8228             if (_t[k]) {
8229                 cfg[k] = _t[k];
8230             }
8231         });
8232         
8233         
8234         if (this.layout) {
8235             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8236         }
8237         
8238         if(this.store || this.cm){
8239             if(this.headerShow){
8240                 cfg.cn.push(this.renderHeader());
8241             }
8242             
8243             cfg.cn.push(this.renderBody());
8244             
8245             if(this.footerShow){
8246                 cfg.cn.push(this.renderFooter());
8247             }
8248             // where does this come from?
8249             //cfg.cls+=  ' TableGrid';
8250         }
8251         
8252         return { cn : [ cfg ] };
8253     },
8254     
8255     initEvents : function()
8256     {   
8257         if(!this.store || !this.cm){
8258             return;
8259         }
8260         if (this.selModel) {
8261             this.selModel.initEvents();
8262         }
8263         
8264         
8265         //Roo.log('initEvents with ds!!!!');
8266         
8267         this.mainBody = this.el.select('tbody', true).first();
8268         this.mainHead = this.el.select('thead', true).first();
8269         this.mainFoot = this.el.select('tfoot', true).first();
8270         
8271         
8272         
8273         var _this = this;
8274         
8275         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8276             e.on('click', _this.sort, _this);
8277         });
8278         
8279         this.mainBody.on("click", this.onClick, this);
8280         this.mainBody.on("dblclick", this.onDblClick, this);
8281         
8282         // why is this done????? = it breaks dialogs??
8283         //this.parent().el.setStyle('position', 'relative');
8284         
8285         
8286         if (this.footer) {
8287             this.footer.parentId = this.id;
8288             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8289             
8290             if(this.lazyLoad){
8291                 this.el.select('tfoot tr td').first().addClass('hide');
8292             }
8293         } 
8294         
8295         if(this.loadMask) {
8296             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8297         }
8298         
8299         this.store.on('load', this.onLoad, this);
8300         this.store.on('beforeload', this.onBeforeLoad, this);
8301         this.store.on('update', this.onUpdate, this);
8302         this.store.on('add', this.onAdd, this);
8303         this.store.on("clear", this.clear, this);
8304         
8305         this.el.on("contextmenu", this.onContextMenu, this);
8306         
8307         this.mainBody.on('scroll', this.onBodyScroll, this);
8308         
8309         this.cm.on("headerchange", this.onHeaderChange, this);
8310         
8311         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8312         
8313     },
8314     
8315     onContextMenu : function(e, t)
8316     {
8317         this.processEvent("contextmenu", e);
8318     },
8319     
8320     processEvent : function(name, e)
8321     {
8322         if (name != 'touchstart' ) {
8323             this.fireEvent(name, e);    
8324         }
8325         
8326         var t = e.getTarget();
8327         
8328         var cell = Roo.get(t);
8329         
8330         if(!cell){
8331             return;
8332         }
8333         
8334         if(cell.findParent('tfoot', false, true)){
8335             return;
8336         }
8337         
8338         if(cell.findParent('thead', false, true)){
8339             
8340             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8341                 cell = Roo.get(t).findParent('th', false, true);
8342                 if (!cell) {
8343                     Roo.log("failed to find th in thead?");
8344                     Roo.log(e.getTarget());
8345                     return;
8346                 }
8347             }
8348             
8349             var cellIndex = cell.dom.cellIndex;
8350             
8351             var ename = name == 'touchstart' ? 'click' : name;
8352             this.fireEvent("header" + ename, this, cellIndex, e);
8353             
8354             return;
8355         }
8356         
8357         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8358             cell = Roo.get(t).findParent('td', false, true);
8359             if (!cell) {
8360                 Roo.log("failed to find th in tbody?");
8361                 Roo.log(e.getTarget());
8362                 return;
8363             }
8364         }
8365         
8366         var row = cell.findParent('tr', false, true);
8367         var cellIndex = cell.dom.cellIndex;
8368         var rowIndex = row.dom.rowIndex - 1;
8369         
8370         if(row !== false){
8371             
8372             this.fireEvent("row" + name, this, rowIndex, e);
8373             
8374             if(cell !== false){
8375             
8376                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8377             }
8378         }
8379         
8380     },
8381     
8382     onMouseover : function(e, el)
8383     {
8384         var cell = Roo.get(el);
8385         
8386         if(!cell){
8387             return;
8388         }
8389         
8390         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8391             cell = cell.findParent('td', false, true);
8392         }
8393         
8394         var row = cell.findParent('tr', false, true);
8395         var cellIndex = cell.dom.cellIndex;
8396         var rowIndex = row.dom.rowIndex - 1; // start from 0
8397         
8398         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8399         
8400     },
8401     
8402     onMouseout : function(e, el)
8403     {
8404         var cell = Roo.get(el);
8405         
8406         if(!cell){
8407             return;
8408         }
8409         
8410         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8411             cell = cell.findParent('td', false, true);
8412         }
8413         
8414         var row = cell.findParent('tr', false, true);
8415         var cellIndex = cell.dom.cellIndex;
8416         var rowIndex = row.dom.rowIndex - 1; // start from 0
8417         
8418         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8419         
8420     },
8421     
8422     onClick : function(e, el)
8423     {
8424         var cell = Roo.get(el);
8425         
8426         if(!cell || (!this.cellSelection && !this.rowSelection)){
8427             return;
8428         }
8429         
8430         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8431             cell = cell.findParent('td', false, true);
8432         }
8433         
8434         if(!cell || typeof(cell) == 'undefined'){
8435             return;
8436         }
8437         
8438         var row = cell.findParent('tr', false, true);
8439         
8440         if(!row || typeof(row) == 'undefined'){
8441             return;
8442         }
8443         
8444         var cellIndex = cell.dom.cellIndex;
8445         var rowIndex = this.getRowIndex(row);
8446         
8447         // why??? - should these not be based on SelectionModel?
8448         if(this.cellSelection){
8449             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8450         }
8451         
8452         if(this.rowSelection){
8453             this.fireEvent('rowclick', this, row, rowIndex, e);
8454         }
8455         
8456         
8457     },
8458         
8459     onDblClick : function(e,el)
8460     {
8461         var cell = Roo.get(el);
8462         
8463         if(!cell || (!this.cellSelection && !this.rowSelection)){
8464             return;
8465         }
8466         
8467         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8468             cell = cell.findParent('td', false, true);
8469         }
8470         
8471         if(!cell || typeof(cell) == 'undefined'){
8472             return;
8473         }
8474         
8475         var row = cell.findParent('tr', false, true);
8476         
8477         if(!row || typeof(row) == 'undefined'){
8478             return;
8479         }
8480         
8481         var cellIndex = cell.dom.cellIndex;
8482         var rowIndex = this.getRowIndex(row);
8483         
8484         if(this.cellSelection){
8485             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8486         }
8487         
8488         if(this.rowSelection){
8489             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8490         }
8491     },
8492     
8493     sort : function(e,el)
8494     {
8495         var col = Roo.get(el);
8496         
8497         if(!col.hasClass('sortable')){
8498             return;
8499         }
8500         
8501         var sort = col.attr('sort');
8502         var dir = 'ASC';
8503         
8504         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8505             dir = 'DESC';
8506         }
8507         
8508         this.store.sortInfo = {field : sort, direction : dir};
8509         
8510         if (this.footer) {
8511             Roo.log("calling footer first");
8512             this.footer.onClick('first');
8513         } else {
8514         
8515             this.store.load({ params : { start : 0 } });
8516         }
8517     },
8518     
8519     renderHeader : function()
8520     {
8521         var header = {
8522             tag: 'thead',
8523             cn : []
8524         };
8525         
8526         var cm = this.cm;
8527         this.totalWidth = 0;
8528         
8529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530             
8531             var config = cm.config[i];
8532             
8533             var c = {
8534                 tag: 'th',
8535                 cls : 'x-hcol-' + i,
8536                 style : '',
8537                 html: cm.getColumnHeader(i)
8538             };
8539             
8540             var hh = '';
8541             
8542             if(typeof(config.sortable) != 'undefined' && config.sortable){
8543                 c.cls = 'sortable';
8544                 c.html = '<i class="far"></i>' + c.html;
8545             }
8546             
8547             // could use BS4 hidden-..-down 
8548             
8549             if(typeof(config.lgHeader) != 'undefined'){
8550                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8551             }
8552             
8553             if(typeof(config.mdHeader) != 'undefined'){
8554                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8555             }
8556             
8557             if(typeof(config.smHeader) != 'undefined'){
8558                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8559             }
8560             
8561             if(typeof(config.xsHeader) != 'undefined'){
8562                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8563             }
8564             
8565             if(hh.length){
8566                 c.html = hh;
8567             }
8568             
8569             if(typeof(config.tooltip) != 'undefined'){
8570                 c.tooltip = config.tooltip;
8571             }
8572             
8573             if(typeof(config.colspan) != 'undefined'){
8574                 c.colspan = config.colspan;
8575             }
8576             
8577             if(typeof(config.hidden) != 'undefined' && config.hidden){
8578                 c.style += ' display:none;';
8579             }
8580             
8581             if(typeof(config.dataIndex) != 'undefined'){
8582                 c.sort = config.dataIndex;
8583             }
8584             
8585            
8586             
8587             if(typeof(config.align) != 'undefined' && config.align.length){
8588                 c.style += ' text-align:' + config.align + ';';
8589             }
8590             
8591             if(typeof(config.width) != 'undefined'){
8592                 c.style += ' width:' + config.width + 'px;';
8593                 this.totalWidth += config.width;
8594             } else {
8595                 this.totalWidth += 100; // assume minimum of 100 per column?
8596             }
8597             
8598             if(typeof(config.cls) != 'undefined'){
8599                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8600             }
8601             
8602             ['xs','sm','md','lg'].map(function(size){
8603                 
8604                 if(typeof(config[size]) == 'undefined'){
8605                     return;
8606                 }
8607                  
8608                 if (!config[size]) { // 0 = hidden
8609                     // BS 4 '0' is treated as hide that column and below.
8610                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8611                     return;
8612                 }
8613                 
8614                 c.cls += ' col-' + size + '-' + config[size] + (
8615                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8616                 );
8617                 
8618                 
8619             });
8620             
8621             header.cn.push(c)
8622         }
8623         
8624         return header;
8625     },
8626     
8627     renderBody : function()
8628     {
8629         var body = {
8630             tag: 'tbody',
8631             cn : [
8632                 {
8633                     tag: 'tr',
8634                     cn : [
8635                         {
8636                             tag : 'td',
8637                             colspan :  this.cm.getColumnCount()
8638                         }
8639                     ]
8640                 }
8641             ]
8642         };
8643         
8644         return body;
8645     },
8646     
8647     renderFooter : function()
8648     {
8649         var footer = {
8650             tag: 'tfoot',
8651             cn : [
8652                 {
8653                     tag: 'tr',
8654                     cn : [
8655                         {
8656                             tag : 'td',
8657                             colspan :  this.cm.getColumnCount()
8658                         }
8659                     ]
8660                 }
8661             ]
8662         };
8663         
8664         return footer;
8665     },
8666     
8667     
8668     
8669     onLoad : function()
8670     {
8671 //        Roo.log('ds onload');
8672         this.clear();
8673         
8674         var _this = this;
8675         var cm = this.cm;
8676         var ds = this.store;
8677         
8678         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8679             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8680             if (_this.store.sortInfo) {
8681                     
8682                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8683                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8684                 }
8685                 
8686                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8687                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8688                 }
8689             }
8690         });
8691         
8692         var tbody =  this.mainBody;
8693               
8694         if(ds.getCount() > 0){
8695             ds.data.each(function(d,rowIndex){
8696                 var row =  this.renderRow(cm, ds, rowIndex);
8697                 
8698                 tbody.createChild(row);
8699                 
8700                 var _this = this;
8701                 
8702                 if(row.cellObjects.length){
8703                     Roo.each(row.cellObjects, function(r){
8704                         _this.renderCellObject(r);
8705                     })
8706                 }
8707                 
8708             }, this);
8709         }
8710         
8711         var tfoot = this.el.select('tfoot', true).first();
8712         
8713         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8714             
8715             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8716             
8717             var total = this.ds.getTotalCount();
8718             
8719             if(this.footer.pageSize < total){
8720                 this.mainFoot.show();
8721             }
8722         }
8723         
8724         Roo.each(this.el.select('tbody td', true).elements, function(e){
8725             e.on('mouseover', _this.onMouseover, _this);
8726         });
8727         
8728         Roo.each(this.el.select('tbody td', true).elements, function(e){
8729             e.on('mouseout', _this.onMouseout, _this);
8730         });
8731         this.fireEvent('rowsrendered', this);
8732         
8733         this.autoSize();
8734     },
8735     
8736     
8737     onUpdate : function(ds,record)
8738     {
8739         this.refreshRow(record);
8740         this.autoSize();
8741     },
8742     
8743     onRemove : function(ds, record, index, isUpdate){
8744         if(isUpdate !== true){
8745             this.fireEvent("beforerowremoved", this, index, record);
8746         }
8747         var bt = this.mainBody.dom;
8748         
8749         var rows = this.el.select('tbody > tr', true).elements;
8750         
8751         if(typeof(rows[index]) != 'undefined'){
8752             bt.removeChild(rows[index].dom);
8753         }
8754         
8755 //        if(bt.rows[index]){
8756 //            bt.removeChild(bt.rows[index]);
8757 //        }
8758         
8759         if(isUpdate !== true){
8760             //this.stripeRows(index);
8761             //this.syncRowHeights(index, index);
8762             //this.layout();
8763             this.fireEvent("rowremoved", this, index, record);
8764         }
8765     },
8766     
8767     onAdd : function(ds, records, rowIndex)
8768     {
8769         //Roo.log('on Add called');
8770         // - note this does not handle multiple adding very well..
8771         var bt = this.mainBody.dom;
8772         for (var i =0 ; i < records.length;i++) {
8773             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8774             //Roo.log(records[i]);
8775             //Roo.log(this.store.getAt(rowIndex+i));
8776             this.insertRow(this.store, rowIndex + i, false);
8777             return;
8778         }
8779         
8780     },
8781     
8782     
8783     refreshRow : function(record){
8784         var ds = this.store, index;
8785         if(typeof record == 'number'){
8786             index = record;
8787             record = ds.getAt(index);
8788         }else{
8789             index = ds.indexOf(record);
8790             if (index < 0) {
8791                 return; // should not happen - but seems to 
8792             }
8793         }
8794         this.insertRow(ds, index, true);
8795         this.autoSize();
8796         this.onRemove(ds, record, index+1, true);
8797         this.autoSize();
8798         //this.syncRowHeights(index, index);
8799         //this.layout();
8800         this.fireEvent("rowupdated", this, index, record);
8801     },
8802     
8803     insertRow : function(dm, rowIndex, isUpdate){
8804         
8805         if(!isUpdate){
8806             this.fireEvent("beforerowsinserted", this, rowIndex);
8807         }
8808             //var s = this.getScrollState();
8809         var row = this.renderRow(this.cm, this.store, rowIndex);
8810         // insert before rowIndex..
8811         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8812         
8813         var _this = this;
8814                 
8815         if(row.cellObjects.length){
8816             Roo.each(row.cellObjects, function(r){
8817                 _this.renderCellObject(r);
8818             })
8819         }
8820             
8821         if(!isUpdate){
8822             this.fireEvent("rowsinserted", this, rowIndex);
8823             //this.syncRowHeights(firstRow, lastRow);
8824             //this.stripeRows(firstRow);
8825             //this.layout();
8826         }
8827         
8828     },
8829     
8830     
8831     getRowDom : function(rowIndex)
8832     {
8833         var rows = this.el.select('tbody > tr', true).elements;
8834         
8835         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8836         
8837     },
8838     // returns the object tree for a tr..
8839   
8840     
8841     renderRow : function(cm, ds, rowIndex) 
8842     {
8843         var d = ds.getAt(rowIndex);
8844         
8845         var row = {
8846             tag : 'tr',
8847             cls : 'x-row-' + rowIndex,
8848             cn : []
8849         };
8850             
8851         var cellObjects = [];
8852         
8853         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8854             var config = cm.config[i];
8855             
8856             var renderer = cm.getRenderer(i);
8857             var value = '';
8858             var id = false;
8859             
8860             if(typeof(renderer) !== 'undefined'){
8861                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8862             }
8863             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8864             // and are rendered into the cells after the row is rendered - using the id for the element.
8865             
8866             if(typeof(value) === 'object'){
8867                 id = Roo.id();
8868                 cellObjects.push({
8869                     container : id,
8870                     cfg : value 
8871                 })
8872             }
8873             
8874             var rowcfg = {
8875                 record: d,
8876                 rowIndex : rowIndex,
8877                 colIndex : i,
8878                 rowClass : ''
8879             };
8880
8881             this.fireEvent('rowclass', this, rowcfg);
8882             
8883             var td = {
8884                 tag: 'td',
8885                 cls : rowcfg.rowClass + ' x-col-' + i,
8886                 style: '',
8887                 html: (typeof(value) === 'object') ? '' : value
8888             };
8889             
8890             if (id) {
8891                 td.id = id;
8892             }
8893             
8894             if(typeof(config.colspan) != 'undefined'){
8895                 td.colspan = config.colspan;
8896             }
8897             
8898             if(typeof(config.hidden) != 'undefined' && config.hidden){
8899                 td.style += ' display:none;';
8900             }
8901             
8902             if(typeof(config.align) != 'undefined' && config.align.length){
8903                 td.style += ' text-align:' + config.align + ';';
8904             }
8905             if(typeof(config.valign) != 'undefined' && config.valign.length){
8906                 td.style += ' vertical-align:' + config.valign + ';';
8907             }
8908             
8909             if(typeof(config.width) != 'undefined'){
8910                 td.style += ' width:' +  config.width + 'px;';
8911             }
8912             
8913             if(typeof(config.cursor) != 'undefined'){
8914                 td.style += ' cursor:' +  config.cursor + ';';
8915             }
8916             
8917             if(typeof(config.cls) != 'undefined'){
8918                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8919             }
8920             
8921             ['xs','sm','md','lg'].map(function(size){
8922                 
8923                 if(typeof(config[size]) == 'undefined'){
8924                     return;
8925                 }
8926                 
8927                 
8928                   
8929                 if (!config[size]) { // 0 = hidden
8930                     // BS 4 '0' is treated as hide that column and below.
8931                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8932                     return;
8933                 }
8934                 
8935                 td.cls += ' col-' + size + '-' + config[size] + (
8936                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8937                 );
8938                  
8939
8940             });
8941             
8942             row.cn.push(td);
8943            
8944         }
8945         
8946         row.cellObjects = cellObjects;
8947         
8948         return row;
8949           
8950     },
8951     
8952     
8953     
8954     onBeforeLoad : function()
8955     {
8956         
8957     },
8958      /**
8959      * Remove all rows
8960      */
8961     clear : function()
8962     {
8963         this.el.select('tbody', true).first().dom.innerHTML = '';
8964     },
8965     /**
8966      * Show or hide a row.
8967      * @param {Number} rowIndex to show or hide
8968      * @param {Boolean} state hide
8969      */
8970     setRowVisibility : function(rowIndex, state)
8971     {
8972         var bt = this.mainBody.dom;
8973         
8974         var rows = this.el.select('tbody > tr', true).elements;
8975         
8976         if(typeof(rows[rowIndex]) == 'undefined'){
8977             return;
8978         }
8979         rows[rowIndex].dom.style.display = state ? '' : 'none';
8980     },
8981     
8982     
8983     getSelectionModel : function(){
8984         if(!this.selModel){
8985             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8986         }
8987         return this.selModel;
8988     },
8989     /*
8990      * Render the Roo.bootstrap object from renderder
8991      */
8992     renderCellObject : function(r)
8993     {
8994         var _this = this;
8995         
8996         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8997         
8998         var t = r.cfg.render(r.container);
8999         
9000         if(r.cfg.cn){
9001             Roo.each(r.cfg.cn, function(c){
9002                 var child = {
9003                     container: t.getChildContainer(),
9004                     cfg: c
9005                 };
9006                 _this.renderCellObject(child);
9007             })
9008         }
9009     },
9010     
9011     getRowIndex : function(row)
9012     {
9013         var rowIndex = -1;
9014         
9015         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9016             if(el != row){
9017                 return;
9018             }
9019             
9020             rowIndex = index;
9021         });
9022         
9023         return rowIndex;
9024     },
9025      /**
9026      * Returns the grid's underlying element = used by panel.Grid
9027      * @return {Element} The element
9028      */
9029     getGridEl : function(){
9030         return this.el;
9031     },
9032      /**
9033      * Forces a resize - used by panel.Grid
9034      * @return {Element} The element
9035      */
9036     autoSize : function()
9037     {
9038         //var ctr = Roo.get(this.container.dom.parentElement);
9039         var ctr = Roo.get(this.el.dom);
9040         
9041         var thd = this.getGridEl().select('thead',true).first();
9042         var tbd = this.getGridEl().select('tbody', true).first();
9043         var tfd = this.getGridEl().select('tfoot', true).first();
9044         
9045         var cw = ctr.getWidth();
9046         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9047         
9048         if (tbd) {
9049             
9050             tbd.setWidth(ctr.getWidth());
9051             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9052             // this needs fixing for various usage - currently only hydra job advers I think..
9053             //tdb.setHeight(
9054             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9055             //); 
9056             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9057             cw -= barsize;
9058         }
9059         cw = Math.max(cw, this.totalWidth);
9060         this.getGridEl().select('tbody tr',true).setWidth(cw);
9061         
9062         // resize 'expandable coloumn?
9063         
9064         return; // we doe not have a view in this design..
9065         
9066     },
9067     onBodyScroll: function()
9068     {
9069         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9070         if(this.mainHead){
9071             this.mainHead.setStyle({
9072                 'position' : 'relative',
9073                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9074             });
9075         }
9076         
9077         if(this.lazyLoad){
9078             
9079             var scrollHeight = this.mainBody.dom.scrollHeight;
9080             
9081             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9082             
9083             var height = this.mainBody.getHeight();
9084             
9085             if(scrollHeight - height == scrollTop) {
9086                 
9087                 var total = this.ds.getTotalCount();
9088                 
9089                 if(this.footer.cursor + this.footer.pageSize < total){
9090                     
9091                     this.footer.ds.load({
9092                         params : {
9093                             start : this.footer.cursor + this.footer.pageSize,
9094                             limit : this.footer.pageSize
9095                         },
9096                         add : true
9097                     });
9098                 }
9099             }
9100             
9101         }
9102     },
9103     
9104     onHeaderChange : function()
9105     {
9106         var header = this.renderHeader();
9107         var table = this.el.select('table', true).first();
9108         
9109         this.mainHead.remove();
9110         this.mainHead = table.createChild(header, this.mainBody, false);
9111     },
9112     
9113     onHiddenChange : function(colModel, colIndex, hidden)
9114     {
9115         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9116         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9117         
9118         this.CSS.updateRule(thSelector, "display", "");
9119         this.CSS.updateRule(tdSelector, "display", "");
9120         
9121         if(hidden){
9122             this.CSS.updateRule(thSelector, "display", "none");
9123             this.CSS.updateRule(tdSelector, "display", "none");
9124         }
9125         
9126         this.onHeaderChange();
9127         this.onLoad();
9128     },
9129     
9130     setColumnWidth: function(col_index, width)
9131     {
9132         // width = "md-2 xs-2..."
9133         if(!this.colModel.config[col_index]) {
9134             return;
9135         }
9136         
9137         var w = width.split(" ");
9138         
9139         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9140         
9141         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9142         
9143         
9144         for(var j = 0; j < w.length; j++) {
9145             
9146             if(!w[j]) {
9147                 continue;
9148             }
9149             
9150             var size_cls = w[j].split("-");
9151             
9152             if(!Number.isInteger(size_cls[1] * 1)) {
9153                 continue;
9154             }
9155             
9156             if(!this.colModel.config[col_index][size_cls[0]]) {
9157                 continue;
9158             }
9159             
9160             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9161                 continue;
9162             }
9163             
9164             h_row[0].classList.replace(
9165                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9166                 "col-"+size_cls[0]+"-"+size_cls[1]
9167             );
9168             
9169             for(var i = 0; i < rows.length; i++) {
9170                 
9171                 var size_cls = w[j].split("-");
9172                 
9173                 if(!Number.isInteger(size_cls[1] * 1)) {
9174                     continue;
9175                 }
9176                 
9177                 if(!this.colModel.config[col_index][size_cls[0]]) {
9178                     continue;
9179                 }
9180                 
9181                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9182                     continue;
9183                 }
9184                 
9185                 rows[i].classList.replace(
9186                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9187                     "col-"+size_cls[0]+"-"+size_cls[1]
9188                 );
9189             }
9190             
9191             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9192         }
9193     }
9194 });
9195
9196  
9197
9198  /*
9199  * - LGPL
9200  *
9201  * table cell
9202  * 
9203  */
9204
9205 /**
9206  * @class Roo.bootstrap.TableCell
9207  * @extends Roo.bootstrap.Component
9208  * Bootstrap TableCell class
9209  * @cfg {String} html cell contain text
9210  * @cfg {String} cls cell class
9211  * @cfg {String} tag cell tag (td|th) default td
9212  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9213  * @cfg {String} align Aligns the content in a cell
9214  * @cfg {String} axis Categorizes cells
9215  * @cfg {String} bgcolor Specifies the background color of a cell
9216  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9217  * @cfg {Number} colspan Specifies the number of columns a cell should span
9218  * @cfg {String} headers Specifies one or more header cells a cell is related to
9219  * @cfg {Number} height Sets the height of a cell
9220  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9221  * @cfg {Number} rowspan Sets the number of rows a cell should span
9222  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9223  * @cfg {String} valign Vertical aligns the content in a cell
9224  * @cfg {Number} width Specifies the width of a cell
9225  * 
9226  * @constructor
9227  * Create a new TableCell
9228  * @param {Object} config The config object
9229  */
9230
9231 Roo.bootstrap.TableCell = function(config){
9232     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9233 };
9234
9235 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9236     
9237     html: false,
9238     cls: false,
9239     tag: false,
9240     abbr: false,
9241     align: false,
9242     axis: false,
9243     bgcolor: false,
9244     charoff: false,
9245     colspan: false,
9246     headers: false,
9247     height: false,
9248     nowrap: false,
9249     rowspan: false,
9250     scope: false,
9251     valign: false,
9252     width: false,
9253     
9254     
9255     getAutoCreate : function(){
9256         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9257         
9258         cfg = {
9259             tag: 'td'
9260         };
9261         
9262         if(this.tag){
9263             cfg.tag = this.tag;
9264         }
9265         
9266         if (this.html) {
9267             cfg.html=this.html
9268         }
9269         if (this.cls) {
9270             cfg.cls=this.cls
9271         }
9272         if (this.abbr) {
9273             cfg.abbr=this.abbr
9274         }
9275         if (this.align) {
9276             cfg.align=this.align
9277         }
9278         if (this.axis) {
9279             cfg.axis=this.axis
9280         }
9281         if (this.bgcolor) {
9282             cfg.bgcolor=this.bgcolor
9283         }
9284         if (this.charoff) {
9285             cfg.charoff=this.charoff
9286         }
9287         if (this.colspan) {
9288             cfg.colspan=this.colspan
9289         }
9290         if (this.headers) {
9291             cfg.headers=this.headers
9292         }
9293         if (this.height) {
9294             cfg.height=this.height
9295         }
9296         if (this.nowrap) {
9297             cfg.nowrap=this.nowrap
9298         }
9299         if (this.rowspan) {
9300             cfg.rowspan=this.rowspan
9301         }
9302         if (this.scope) {
9303             cfg.scope=this.scope
9304         }
9305         if (this.valign) {
9306             cfg.valign=this.valign
9307         }
9308         if (this.width) {
9309             cfg.width=this.width
9310         }
9311         
9312         
9313         return cfg;
9314     }
9315    
9316 });
9317
9318  
9319
9320  /*
9321  * - LGPL
9322  *
9323  * table row
9324  * 
9325  */
9326
9327 /**
9328  * @class Roo.bootstrap.TableRow
9329  * @extends Roo.bootstrap.Component
9330  * Bootstrap TableRow class
9331  * @cfg {String} cls row class
9332  * @cfg {String} align Aligns the content in a table row
9333  * @cfg {String} bgcolor Specifies a background color for a table row
9334  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9335  * @cfg {String} valign Vertical aligns the content in a table row
9336  * 
9337  * @constructor
9338  * Create a new TableRow
9339  * @param {Object} config The config object
9340  */
9341
9342 Roo.bootstrap.TableRow = function(config){
9343     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9344 };
9345
9346 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9347     
9348     cls: false,
9349     align: false,
9350     bgcolor: false,
9351     charoff: false,
9352     valign: false,
9353     
9354     getAutoCreate : function(){
9355         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9356         
9357         cfg = {
9358             tag: 'tr'
9359         };
9360             
9361         if(this.cls){
9362             cfg.cls = this.cls;
9363         }
9364         if(this.align){
9365             cfg.align = this.align;
9366         }
9367         if(this.bgcolor){
9368             cfg.bgcolor = this.bgcolor;
9369         }
9370         if(this.charoff){
9371             cfg.charoff = this.charoff;
9372         }
9373         if(this.valign){
9374             cfg.valign = this.valign;
9375         }
9376         
9377         return cfg;
9378     }
9379    
9380 });
9381
9382  
9383
9384  /*
9385  * - LGPL
9386  *
9387  * table body
9388  * 
9389  */
9390
9391 /**
9392  * @class Roo.bootstrap.TableBody
9393  * @extends Roo.bootstrap.Component
9394  * Bootstrap TableBody class
9395  * @cfg {String} cls element class
9396  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9397  * @cfg {String} align Aligns the content inside the element
9398  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9399  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9400  * 
9401  * @constructor
9402  * Create a new TableBody
9403  * @param {Object} config The config object
9404  */
9405
9406 Roo.bootstrap.TableBody = function(config){
9407     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9408 };
9409
9410 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9411     
9412     cls: false,
9413     tag: false,
9414     align: false,
9415     charoff: false,
9416     valign: false,
9417     
9418     getAutoCreate : function(){
9419         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9420         
9421         cfg = {
9422             tag: 'tbody'
9423         };
9424             
9425         if (this.cls) {
9426             cfg.cls=this.cls
9427         }
9428         if(this.tag){
9429             cfg.tag = this.tag;
9430         }
9431         
9432         if(this.align){
9433             cfg.align = this.align;
9434         }
9435         if(this.charoff){
9436             cfg.charoff = this.charoff;
9437         }
9438         if(this.valign){
9439             cfg.valign = this.valign;
9440         }
9441         
9442         return cfg;
9443     }
9444     
9445     
9446 //    initEvents : function()
9447 //    {
9448 //        
9449 //        if(!this.store){
9450 //            return;
9451 //        }
9452 //        
9453 //        this.store = Roo.factory(this.store, Roo.data);
9454 //        this.store.on('load', this.onLoad, this);
9455 //        
9456 //        this.store.load();
9457 //        
9458 //    },
9459 //    
9460 //    onLoad: function () 
9461 //    {   
9462 //        this.fireEvent('load', this);
9463 //    }
9464 //    
9465 //   
9466 });
9467
9468  
9469
9470  /*
9471  * Based on:
9472  * Ext JS Library 1.1.1
9473  * Copyright(c) 2006-2007, Ext JS, LLC.
9474  *
9475  * Originally Released Under LGPL - original licence link has changed is not relivant.
9476  *
9477  * Fork - LGPL
9478  * <script type="text/javascript">
9479  */
9480
9481 // as we use this in bootstrap.
9482 Roo.namespace('Roo.form');
9483  /**
9484  * @class Roo.form.Action
9485  * Internal Class used to handle form actions
9486  * @constructor
9487  * @param {Roo.form.BasicForm} el The form element or its id
9488  * @param {Object} config Configuration options
9489  */
9490
9491  
9492  
9493 // define the action interface
9494 Roo.form.Action = function(form, options){
9495     this.form = form;
9496     this.options = options || {};
9497 };
9498 /**
9499  * Client Validation Failed
9500  * @const 
9501  */
9502 Roo.form.Action.CLIENT_INVALID = 'client';
9503 /**
9504  * Server Validation Failed
9505  * @const 
9506  */
9507 Roo.form.Action.SERVER_INVALID = 'server';
9508  /**
9509  * Connect to Server Failed
9510  * @const 
9511  */
9512 Roo.form.Action.CONNECT_FAILURE = 'connect';
9513 /**
9514  * Reading Data from Server Failed
9515  * @const 
9516  */
9517 Roo.form.Action.LOAD_FAILURE = 'load';
9518
9519 Roo.form.Action.prototype = {
9520     type : 'default',
9521     failureType : undefined,
9522     response : undefined,
9523     result : undefined,
9524
9525     // interface method
9526     run : function(options){
9527
9528     },
9529
9530     // interface method
9531     success : function(response){
9532
9533     },
9534
9535     // interface method
9536     handleResponse : function(response){
9537
9538     },
9539
9540     // default connection failure
9541     failure : function(response){
9542         
9543         this.response = response;
9544         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9545         this.form.afterAction(this, false);
9546     },
9547
9548     processResponse : function(response){
9549         this.response = response;
9550         if(!response.responseText){
9551             return true;
9552         }
9553         this.result = this.handleResponse(response);
9554         return this.result;
9555     },
9556
9557     // utility functions used internally
9558     getUrl : function(appendParams){
9559         var url = this.options.url || this.form.url || this.form.el.dom.action;
9560         if(appendParams){
9561             var p = this.getParams();
9562             if(p){
9563                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9564             }
9565         }
9566         return url;
9567     },
9568
9569     getMethod : function(){
9570         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9571     },
9572
9573     getParams : function(){
9574         var bp = this.form.baseParams;
9575         var p = this.options.params;
9576         if(p){
9577             if(typeof p == "object"){
9578                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9579             }else if(typeof p == 'string' && bp){
9580                 p += '&' + Roo.urlEncode(bp);
9581             }
9582         }else if(bp){
9583             p = Roo.urlEncode(bp);
9584         }
9585         return p;
9586     },
9587
9588     createCallback : function(){
9589         return {
9590             success: this.success,
9591             failure: this.failure,
9592             scope: this,
9593             timeout: (this.form.timeout*1000),
9594             upload: this.form.fileUpload ? this.success : undefined
9595         };
9596     }
9597 };
9598
9599 Roo.form.Action.Submit = function(form, options){
9600     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9601 };
9602
9603 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9604     type : 'submit',
9605
9606     haveProgress : false,
9607     uploadComplete : false,
9608     
9609     // uploadProgress indicator.
9610     uploadProgress : function()
9611     {
9612         if (!this.form.progressUrl) {
9613             return;
9614         }
9615         
9616         if (!this.haveProgress) {
9617             Roo.MessageBox.progress("Uploading", "Uploading");
9618         }
9619         if (this.uploadComplete) {
9620            Roo.MessageBox.hide();
9621            return;
9622         }
9623         
9624         this.haveProgress = true;
9625    
9626         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9627         
9628         var c = new Roo.data.Connection();
9629         c.request({
9630             url : this.form.progressUrl,
9631             params: {
9632                 id : uid
9633             },
9634             method: 'GET',
9635             success : function(req){
9636                //console.log(data);
9637                 var rdata = false;
9638                 var edata;
9639                 try  {
9640                    rdata = Roo.decode(req.responseText)
9641                 } catch (e) {
9642                     Roo.log("Invalid data from server..");
9643                     Roo.log(edata);
9644                     return;
9645                 }
9646                 if (!rdata || !rdata.success) {
9647                     Roo.log(rdata);
9648                     Roo.MessageBox.alert(Roo.encode(rdata));
9649                     return;
9650                 }
9651                 var data = rdata.data;
9652                 
9653                 if (this.uploadComplete) {
9654                    Roo.MessageBox.hide();
9655                    return;
9656                 }
9657                    
9658                 if (data){
9659                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9660                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9661                     );
9662                 }
9663                 this.uploadProgress.defer(2000,this);
9664             },
9665        
9666             failure: function(data) {
9667                 Roo.log('progress url failed ');
9668                 Roo.log(data);
9669             },
9670             scope : this
9671         });
9672            
9673     },
9674     
9675     
9676     run : function()
9677     {
9678         // run get Values on the form, so it syncs any secondary forms.
9679         this.form.getValues();
9680         
9681         var o = this.options;
9682         var method = this.getMethod();
9683         var isPost = method == 'POST';
9684         if(o.clientValidation === false || this.form.isValid()){
9685             
9686             if (this.form.progressUrl) {
9687                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9688                     (new Date() * 1) + '' + Math.random());
9689                     
9690             } 
9691             
9692             
9693             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9694                 form:this.form.el.dom,
9695                 url:this.getUrl(!isPost),
9696                 method: method,
9697                 params:isPost ? this.getParams() : null,
9698                 isUpload: this.form.fileUpload,
9699                 formData : this.form.formData
9700             }));
9701             
9702             this.uploadProgress();
9703
9704         }else if (o.clientValidation !== false){ // client validation failed
9705             this.failureType = Roo.form.Action.CLIENT_INVALID;
9706             this.form.afterAction(this, false);
9707         }
9708     },
9709
9710     success : function(response)
9711     {
9712         this.uploadComplete= true;
9713         if (this.haveProgress) {
9714             Roo.MessageBox.hide();
9715         }
9716         
9717         
9718         var result = this.processResponse(response);
9719         if(result === true || result.success){
9720             this.form.afterAction(this, true);
9721             return;
9722         }
9723         if(result.errors){
9724             this.form.markInvalid(result.errors);
9725             this.failureType = Roo.form.Action.SERVER_INVALID;
9726         }
9727         this.form.afterAction(this, false);
9728     },
9729     failure : function(response)
9730     {
9731         this.uploadComplete= true;
9732         if (this.haveProgress) {
9733             Roo.MessageBox.hide();
9734         }
9735         
9736         this.response = response;
9737         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9738         this.form.afterAction(this, false);
9739     },
9740     
9741     handleResponse : function(response){
9742         if(this.form.errorReader){
9743             var rs = this.form.errorReader.read(response);
9744             var errors = [];
9745             if(rs.records){
9746                 for(var i = 0, len = rs.records.length; i < len; i++) {
9747                     var r = rs.records[i];
9748                     errors[i] = r.data;
9749                 }
9750             }
9751             if(errors.length < 1){
9752                 errors = null;
9753             }
9754             return {
9755                 success : rs.success,
9756                 errors : errors
9757             };
9758         }
9759         var ret = false;
9760         try {
9761             ret = Roo.decode(response.responseText);
9762         } catch (e) {
9763             ret = {
9764                 success: false,
9765                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9766                 errors : []
9767             };
9768         }
9769         return ret;
9770         
9771     }
9772 });
9773
9774
9775 Roo.form.Action.Load = function(form, options){
9776     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9777     this.reader = this.form.reader;
9778 };
9779
9780 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9781     type : 'load',
9782
9783     run : function(){
9784         
9785         Roo.Ajax.request(Roo.apply(
9786                 this.createCallback(), {
9787                     method:this.getMethod(),
9788                     url:this.getUrl(false),
9789                     params:this.getParams()
9790         }));
9791     },
9792
9793     success : function(response){
9794         
9795         var result = this.processResponse(response);
9796         if(result === true || !result.success || !result.data){
9797             this.failureType = Roo.form.Action.LOAD_FAILURE;
9798             this.form.afterAction(this, false);
9799             return;
9800         }
9801         this.form.clearInvalid();
9802         this.form.setValues(result.data);
9803         this.form.afterAction(this, true);
9804     },
9805
9806     handleResponse : function(response){
9807         if(this.form.reader){
9808             var rs = this.form.reader.read(response);
9809             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9810             return {
9811                 success : rs.success,
9812                 data : data
9813             };
9814         }
9815         return Roo.decode(response.responseText);
9816     }
9817 });
9818
9819 Roo.form.Action.ACTION_TYPES = {
9820     'load' : Roo.form.Action.Load,
9821     'submit' : Roo.form.Action.Submit
9822 };/*
9823  * - LGPL
9824  *
9825  * form
9826  *
9827  */
9828
9829 /**
9830  * @class Roo.bootstrap.Form
9831  * @extends Roo.bootstrap.Component
9832  * Bootstrap Form class
9833  * @cfg {String} method  GET | POST (default POST)
9834  * @cfg {String} labelAlign top | left (default top)
9835  * @cfg {String} align left  | right - for navbars
9836  * @cfg {Boolean} loadMask load mask when submit (default true)
9837
9838  *
9839  * @constructor
9840  * Create a new Form
9841  * @param {Object} config The config object
9842  */
9843
9844
9845 Roo.bootstrap.Form = function(config){
9846     
9847     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9848     
9849     Roo.bootstrap.Form.popover.apply();
9850     
9851     this.addEvents({
9852         /**
9853          * @event clientvalidation
9854          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9855          * @param {Form} this
9856          * @param {Boolean} valid true if the form has passed client-side validation
9857          */
9858         clientvalidation: true,
9859         /**
9860          * @event beforeaction
9861          * Fires before any action is performed. Return false to cancel the action.
9862          * @param {Form} this
9863          * @param {Action} action The action to be performed
9864          */
9865         beforeaction: true,
9866         /**
9867          * @event actionfailed
9868          * Fires when an action fails.
9869          * @param {Form} this
9870          * @param {Action} action The action that failed
9871          */
9872         actionfailed : true,
9873         /**
9874          * @event actioncomplete
9875          * Fires when an action is completed.
9876          * @param {Form} this
9877          * @param {Action} action The action that completed
9878          */
9879         actioncomplete : true
9880     });
9881 };
9882
9883 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9884
9885      /**
9886      * @cfg {String} method
9887      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9888      */
9889     method : 'POST',
9890     /**
9891      * @cfg {String} url
9892      * The URL to use for form actions if one isn't supplied in the action options.
9893      */
9894     /**
9895      * @cfg {Boolean} fileUpload
9896      * Set to true if this form is a file upload.
9897      */
9898
9899     /**
9900      * @cfg {Object} baseParams
9901      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9902      */
9903
9904     /**
9905      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9906      */
9907     timeout: 30,
9908     /**
9909      * @cfg {Sting} align (left|right) for navbar forms
9910      */
9911     align : 'left',
9912
9913     // private
9914     activeAction : null,
9915
9916     /**
9917      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9918      * element by passing it or its id or mask the form itself by passing in true.
9919      * @type Mixed
9920      */
9921     waitMsgTarget : false,
9922
9923     loadMask : true,
9924     
9925     /**
9926      * @cfg {Boolean} errorMask (true|false) default false
9927      */
9928     errorMask : false,
9929     
9930     /**
9931      * @cfg {Number} maskOffset Default 100
9932      */
9933     maskOffset : 100,
9934     
9935     /**
9936      * @cfg {Boolean} maskBody
9937      */
9938     maskBody : false,
9939
9940     getAutoCreate : function(){
9941
9942         var cfg = {
9943             tag: 'form',
9944             method : this.method || 'POST',
9945             id : this.id || Roo.id(),
9946             cls : ''
9947         };
9948         if (this.parent().xtype.match(/^Nav/)) {
9949             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9950
9951         }
9952
9953         if (this.labelAlign == 'left' ) {
9954             cfg.cls += ' form-horizontal';
9955         }
9956
9957
9958         return cfg;
9959     },
9960     initEvents : function()
9961     {
9962         this.el.on('submit', this.onSubmit, this);
9963         // this was added as random key presses on the form where triggering form submit.
9964         this.el.on('keypress', function(e) {
9965             if (e.getCharCode() != 13) {
9966                 return true;
9967             }
9968             // we might need to allow it for textareas.. and some other items.
9969             // check e.getTarget().
9970
9971             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9972                 return true;
9973             }
9974
9975             Roo.log("keypress blocked");
9976
9977             e.preventDefault();
9978             return false;
9979         });
9980         
9981     },
9982     // private
9983     onSubmit : function(e){
9984         e.stopEvent();
9985     },
9986
9987      /**
9988      * Returns true if client-side validation on the form is successful.
9989      * @return Boolean
9990      */
9991     isValid : function(){
9992         var items = this.getItems();
9993         var valid = true;
9994         var target = false;
9995         
9996         items.each(function(f){
9997             
9998             if(f.validate()){
9999                 return;
10000             }
10001             
10002             Roo.log('invalid field: ' + f.name);
10003             
10004             valid = false;
10005
10006             if(!target && f.el.isVisible(true)){
10007                 target = f;
10008             }
10009            
10010         });
10011         
10012         if(this.errorMask && !valid){
10013             Roo.bootstrap.Form.popover.mask(this, target);
10014         }
10015         
10016         return valid;
10017     },
10018     
10019     /**
10020      * Returns true if any fields in this form have changed since their original load.
10021      * @return Boolean
10022      */
10023     isDirty : function(){
10024         var dirty = false;
10025         var items = this.getItems();
10026         items.each(function(f){
10027            if(f.isDirty()){
10028                dirty = true;
10029                return false;
10030            }
10031            return true;
10032         });
10033         return dirty;
10034     },
10035      /**
10036      * Performs a predefined action (submit or load) or custom actions you define on this form.
10037      * @param {String} actionName The name of the action type
10038      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10039      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10040      * accept other config options):
10041      * <pre>
10042 Property          Type             Description
10043 ----------------  ---------------  ----------------------------------------------------------------------------------
10044 url               String           The url for the action (defaults to the form's url)
10045 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10046 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10047 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10048                                    validate the form on the client (defaults to false)
10049      * </pre>
10050      * @return {BasicForm} this
10051      */
10052     doAction : function(action, options){
10053         if(typeof action == 'string'){
10054             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10055         }
10056         if(this.fireEvent('beforeaction', this, action) !== false){
10057             this.beforeAction(action);
10058             action.run.defer(100, action);
10059         }
10060         return this;
10061     },
10062
10063     // private
10064     beforeAction : function(action){
10065         var o = action.options;
10066         
10067         if(this.loadMask){
10068             
10069             if(this.maskBody){
10070                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10071             } else {
10072                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10073             }
10074         }
10075         // not really supported yet.. ??
10076
10077         //if(this.waitMsgTarget === true){
10078         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10079         //}else if(this.waitMsgTarget){
10080         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10081         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10082         //}else {
10083         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10084        // }
10085
10086     },
10087
10088     // private
10089     afterAction : function(action, success){
10090         this.activeAction = null;
10091         var o = action.options;
10092
10093         if(this.loadMask){
10094             
10095             if(this.maskBody){
10096                 Roo.get(document.body).unmask();
10097             } else {
10098                 this.el.unmask();
10099             }
10100         }
10101         
10102         //if(this.waitMsgTarget === true){
10103 //            this.el.unmask();
10104         //}else if(this.waitMsgTarget){
10105         //    this.waitMsgTarget.unmask();
10106         //}else{
10107         //    Roo.MessageBox.updateProgress(1);
10108         //    Roo.MessageBox.hide();
10109        // }
10110         //
10111         if(success){
10112             if(o.reset){
10113                 this.reset();
10114             }
10115             Roo.callback(o.success, o.scope, [this, action]);
10116             this.fireEvent('actioncomplete', this, action);
10117
10118         }else{
10119
10120             // failure condition..
10121             // we have a scenario where updates need confirming.
10122             // eg. if a locking scenario exists..
10123             // we look for { errors : { needs_confirm : true }} in the response.
10124             if (
10125                 (typeof(action.result) != 'undefined')  &&
10126                 (typeof(action.result.errors) != 'undefined')  &&
10127                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10128            ){
10129                 var _t = this;
10130                 Roo.log("not supported yet");
10131                  /*
10132
10133                 Roo.MessageBox.confirm(
10134                     "Change requires confirmation",
10135                     action.result.errorMsg,
10136                     function(r) {
10137                         if (r != 'yes') {
10138                             return;
10139                         }
10140                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10141                     }
10142
10143                 );
10144                 */
10145
10146
10147                 return;
10148             }
10149
10150             Roo.callback(o.failure, o.scope, [this, action]);
10151             // show an error message if no failed handler is set..
10152             if (!this.hasListener('actionfailed')) {
10153                 Roo.log("need to add dialog support");
10154                 /*
10155                 Roo.MessageBox.alert("Error",
10156                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10157                         action.result.errorMsg :
10158                         "Saving Failed, please check your entries or try again"
10159                 );
10160                 */
10161             }
10162
10163             this.fireEvent('actionfailed', this, action);
10164         }
10165
10166     },
10167     /**
10168      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10169      * @param {String} id The value to search for
10170      * @return Field
10171      */
10172     findField : function(id){
10173         var items = this.getItems();
10174         var field = items.get(id);
10175         if(!field){
10176              items.each(function(f){
10177                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10178                     field = f;
10179                     return false;
10180                 }
10181                 return true;
10182             });
10183         }
10184         return field || null;
10185     },
10186      /**
10187      * Mark fields in this form invalid in bulk.
10188      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10189      * @return {BasicForm} this
10190      */
10191     markInvalid : function(errors){
10192         if(errors instanceof Array){
10193             for(var i = 0, len = errors.length; i < len; i++){
10194                 var fieldError = errors[i];
10195                 var f = this.findField(fieldError.id);
10196                 if(f){
10197                     f.markInvalid(fieldError.msg);
10198                 }
10199             }
10200         }else{
10201             var field, id;
10202             for(id in errors){
10203                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10204                     field.markInvalid(errors[id]);
10205                 }
10206             }
10207         }
10208         //Roo.each(this.childForms || [], function (f) {
10209         //    f.markInvalid(errors);
10210         //});
10211
10212         return this;
10213     },
10214
10215     /**
10216      * Set values for fields in this form in bulk.
10217      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10218      * @return {BasicForm} this
10219      */
10220     setValues : function(values){
10221         if(values instanceof Array){ // array of objects
10222             for(var i = 0, len = values.length; i < len; i++){
10223                 var v = values[i];
10224                 var f = this.findField(v.id);
10225                 if(f){
10226                     f.setValue(v.value);
10227                     if(this.trackResetOnLoad){
10228                         f.originalValue = f.getValue();
10229                     }
10230                 }
10231             }
10232         }else{ // object hash
10233             var field, id;
10234             for(id in values){
10235                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10236
10237                     if (field.setFromData &&
10238                         field.valueField &&
10239                         field.displayField &&
10240                         // combos' with local stores can
10241                         // be queried via setValue()
10242                         // to set their value..
10243                         (field.store && !field.store.isLocal)
10244                         ) {
10245                         // it's a combo
10246                         var sd = { };
10247                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10248                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10249                         field.setFromData(sd);
10250
10251                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10252                         
10253                         field.setFromData(values);
10254                         
10255                     } else {
10256                         field.setValue(values[id]);
10257                     }
10258
10259
10260                     if(this.trackResetOnLoad){
10261                         field.originalValue = field.getValue();
10262                     }
10263                 }
10264             }
10265         }
10266
10267         //Roo.each(this.childForms || [], function (f) {
10268         //    f.setValues(values);
10269         //});
10270
10271         return this;
10272     },
10273
10274     /**
10275      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10276      * they are returned as an array.
10277      * @param {Boolean} asString
10278      * @return {Object}
10279      */
10280     getValues : function(asString){
10281         //if (this.childForms) {
10282             // copy values from the child forms
10283         //    Roo.each(this.childForms, function (f) {
10284         //        this.setValues(f.getValues());
10285         //    }, this);
10286         //}
10287
10288
10289
10290         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10291         if(asString === true){
10292             return fs;
10293         }
10294         return Roo.urlDecode(fs);
10295     },
10296
10297     /**
10298      * Returns the fields in this form as an object with key/value pairs.
10299      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10300      * @return {Object}
10301      */
10302     getFieldValues : function(with_hidden)
10303     {
10304         var items = this.getItems();
10305         var ret = {};
10306         items.each(function(f){
10307             
10308             if (!f.getName()) {
10309                 return;
10310             }
10311             
10312             var v = f.getValue();
10313             
10314             if (f.inputType =='radio') {
10315                 if (typeof(ret[f.getName()]) == 'undefined') {
10316                     ret[f.getName()] = ''; // empty..
10317                 }
10318
10319                 if (!f.el.dom.checked) {
10320                     return;
10321
10322                 }
10323                 v = f.el.dom.value;
10324
10325             }
10326             
10327             if(f.xtype == 'MoneyField'){
10328                 ret[f.currencyName] = f.getCurrency();
10329             }
10330
10331             // not sure if this supported any more..
10332             if ((typeof(v) == 'object') && f.getRawValue) {
10333                 v = f.getRawValue() ; // dates..
10334             }
10335             // combo boxes where name != hiddenName...
10336             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10337                 ret[f.name] = f.getRawValue();
10338             }
10339             ret[f.getName()] = v;
10340         });
10341
10342         return ret;
10343     },
10344
10345     /**
10346      * Clears all invalid messages in this form.
10347      * @return {BasicForm} this
10348      */
10349     clearInvalid : function(){
10350         var items = this.getItems();
10351
10352         items.each(function(f){
10353            f.clearInvalid();
10354         });
10355
10356         return this;
10357     },
10358
10359     /**
10360      * Resets this form.
10361      * @return {BasicForm} this
10362      */
10363     reset : function(){
10364         var items = this.getItems();
10365         items.each(function(f){
10366             f.reset();
10367         });
10368
10369         Roo.each(this.childForms || [], function (f) {
10370             f.reset();
10371         });
10372
10373
10374         return this;
10375     },
10376     
10377     getItems : function()
10378     {
10379         var r=new Roo.util.MixedCollection(false, function(o){
10380             return o.id || (o.id = Roo.id());
10381         });
10382         var iter = function(el) {
10383             if (el.inputEl) {
10384                 r.add(el);
10385             }
10386             if (!el.items) {
10387                 return;
10388             }
10389             Roo.each(el.items,function(e) {
10390                 iter(e);
10391             });
10392         };
10393
10394         iter(this);
10395         return r;
10396     },
10397     
10398     hideFields : function(items)
10399     {
10400         Roo.each(items, function(i){
10401             
10402             var f = this.findField(i);
10403             
10404             if(!f){
10405                 return;
10406             }
10407             
10408             f.hide();
10409             
10410         }, this);
10411     },
10412     
10413     showFields : function(items)
10414     {
10415         Roo.each(items, function(i){
10416             
10417             var f = this.findField(i);
10418             
10419             if(!f){
10420                 return;
10421             }
10422             
10423             f.show();
10424             
10425         }, this);
10426     }
10427
10428 });
10429
10430 Roo.apply(Roo.bootstrap.Form, {
10431     
10432     popover : {
10433         
10434         padding : 5,
10435         
10436         isApplied : false,
10437         
10438         isMasked : false,
10439         
10440         form : false,
10441         
10442         target : false,
10443         
10444         toolTip : false,
10445         
10446         intervalID : false,
10447         
10448         maskEl : false,
10449         
10450         apply : function()
10451         {
10452             if(this.isApplied){
10453                 return;
10454             }
10455             
10456             this.maskEl = {
10457                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10458                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10459                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10460                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10461             };
10462             
10463             this.maskEl.top.enableDisplayMode("block");
10464             this.maskEl.left.enableDisplayMode("block");
10465             this.maskEl.bottom.enableDisplayMode("block");
10466             this.maskEl.right.enableDisplayMode("block");
10467             
10468             this.toolTip = new Roo.bootstrap.Tooltip({
10469                 cls : 'roo-form-error-popover',
10470                 alignment : {
10471                     'left' : ['r-l', [-2,0], 'right'],
10472                     'right' : ['l-r', [2,0], 'left'],
10473                     'bottom' : ['tl-bl', [0,2], 'top'],
10474                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10475                 }
10476             });
10477             
10478             this.toolTip.render(Roo.get(document.body));
10479
10480             this.toolTip.el.enableDisplayMode("block");
10481             
10482             Roo.get(document.body).on('click', function(){
10483                 this.unmask();
10484             }, this);
10485             
10486             Roo.get(document.body).on('touchstart', function(){
10487                 this.unmask();
10488             }, this);
10489             
10490             this.isApplied = true
10491         },
10492         
10493         mask : function(form, target)
10494         {
10495             this.form = form;
10496             
10497             this.target = target;
10498             
10499             if(!this.form.errorMask || !target.el){
10500                 return;
10501             }
10502             
10503             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10504             
10505             Roo.log(scrollable);
10506             
10507             var ot = this.target.el.calcOffsetsTo(scrollable);
10508             
10509             var scrollTo = ot[1] - this.form.maskOffset;
10510             
10511             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10512             
10513             scrollable.scrollTo('top', scrollTo);
10514             
10515             var box = this.target.el.getBox();
10516             Roo.log(box);
10517             var zIndex = Roo.bootstrap.Modal.zIndex++;
10518
10519             
10520             this.maskEl.top.setStyle('position', 'absolute');
10521             this.maskEl.top.setStyle('z-index', zIndex);
10522             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10523             this.maskEl.top.setLeft(0);
10524             this.maskEl.top.setTop(0);
10525             this.maskEl.top.show();
10526             
10527             this.maskEl.left.setStyle('position', 'absolute');
10528             this.maskEl.left.setStyle('z-index', zIndex);
10529             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10530             this.maskEl.left.setLeft(0);
10531             this.maskEl.left.setTop(box.y - this.padding);
10532             this.maskEl.left.show();
10533
10534             this.maskEl.bottom.setStyle('position', 'absolute');
10535             this.maskEl.bottom.setStyle('z-index', zIndex);
10536             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10537             this.maskEl.bottom.setLeft(0);
10538             this.maskEl.bottom.setTop(box.bottom + this.padding);
10539             this.maskEl.bottom.show();
10540
10541             this.maskEl.right.setStyle('position', 'absolute');
10542             this.maskEl.right.setStyle('z-index', zIndex);
10543             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10544             this.maskEl.right.setLeft(box.right + this.padding);
10545             this.maskEl.right.setTop(box.y - this.padding);
10546             this.maskEl.right.show();
10547
10548             this.toolTip.bindEl = this.target.el;
10549
10550             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10551
10552             var tip = this.target.blankText;
10553
10554             if(this.target.getValue() !== '' ) {
10555                 
10556                 if (this.target.invalidText.length) {
10557                     tip = this.target.invalidText;
10558                 } else if (this.target.regexText.length){
10559                     tip = this.target.regexText;
10560                 }
10561             }
10562
10563             this.toolTip.show(tip);
10564
10565             this.intervalID = window.setInterval(function() {
10566                 Roo.bootstrap.Form.popover.unmask();
10567             }, 10000);
10568
10569             window.onwheel = function(){ return false;};
10570             
10571             (function(){ this.isMasked = true; }).defer(500, this);
10572             
10573         },
10574         
10575         unmask : function()
10576         {
10577             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10578                 return;
10579             }
10580             
10581             this.maskEl.top.setStyle('position', 'absolute');
10582             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10583             this.maskEl.top.hide();
10584
10585             this.maskEl.left.setStyle('position', 'absolute');
10586             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10587             this.maskEl.left.hide();
10588
10589             this.maskEl.bottom.setStyle('position', 'absolute');
10590             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10591             this.maskEl.bottom.hide();
10592
10593             this.maskEl.right.setStyle('position', 'absolute');
10594             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10595             this.maskEl.right.hide();
10596             
10597             this.toolTip.hide();
10598             
10599             this.toolTip.el.hide();
10600             
10601             window.onwheel = function(){ return true;};
10602             
10603             if(this.intervalID){
10604                 window.clearInterval(this.intervalID);
10605                 this.intervalID = false;
10606             }
10607             
10608             this.isMasked = false;
10609             
10610         }
10611         
10612     }
10613     
10614 });
10615
10616 /*
10617  * Based on:
10618  * Ext JS Library 1.1.1
10619  * Copyright(c) 2006-2007, Ext JS, LLC.
10620  *
10621  * Originally Released Under LGPL - original licence link has changed is not relivant.
10622  *
10623  * Fork - LGPL
10624  * <script type="text/javascript">
10625  */
10626 /**
10627  * @class Roo.form.VTypes
10628  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10629  * @singleton
10630  */
10631 Roo.form.VTypes = function(){
10632     // closure these in so they are only created once.
10633     var alpha = /^[a-zA-Z_]+$/;
10634     var alphanum = /^[a-zA-Z0-9_]+$/;
10635     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10636     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10637
10638     // All these messages and functions are configurable
10639     return {
10640         /**
10641          * The function used to validate email addresses
10642          * @param {String} value The email address
10643          */
10644         'email' : function(v){
10645             return email.test(v);
10646         },
10647         /**
10648          * The error text to display when the email validation function returns false
10649          * @type String
10650          */
10651         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10652         /**
10653          * The keystroke filter mask to be applied on email input
10654          * @type RegExp
10655          */
10656         'emailMask' : /[a-z0-9_\.\-@]/i,
10657
10658         /**
10659          * The function used to validate URLs
10660          * @param {String} value The URL
10661          */
10662         'url' : function(v){
10663             return url.test(v);
10664         },
10665         /**
10666          * The error text to display when the url validation function returns false
10667          * @type String
10668          */
10669         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10670         
10671         /**
10672          * The function used to validate alpha values
10673          * @param {String} value The value
10674          */
10675         'alpha' : function(v){
10676             return alpha.test(v);
10677         },
10678         /**
10679          * The error text to display when the alpha validation function returns false
10680          * @type String
10681          */
10682         'alphaText' : 'This field should only contain letters and _',
10683         /**
10684          * The keystroke filter mask to be applied on alpha input
10685          * @type RegExp
10686          */
10687         'alphaMask' : /[a-z_]/i,
10688
10689         /**
10690          * The function used to validate alphanumeric values
10691          * @param {String} value The value
10692          */
10693         'alphanum' : function(v){
10694             return alphanum.test(v);
10695         },
10696         /**
10697          * The error text to display when the alphanumeric validation function returns false
10698          * @type String
10699          */
10700         'alphanumText' : 'This field should only contain letters, numbers and _',
10701         /**
10702          * The keystroke filter mask to be applied on alphanumeric input
10703          * @type RegExp
10704          */
10705         'alphanumMask' : /[a-z0-9_]/i
10706     };
10707 }();/*
10708  * - LGPL
10709  *
10710  * Input
10711  * 
10712  */
10713
10714 /**
10715  * @class Roo.bootstrap.Input
10716  * @extends Roo.bootstrap.Component
10717  * Bootstrap Input class
10718  * @cfg {Boolean} disabled is it disabled
10719  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10720  * @cfg {String} name name of the input
10721  * @cfg {string} fieldLabel - the label associated
10722  * @cfg {string} placeholder - placeholder to put in text.
10723  * @cfg {string}  before - input group add on before
10724  * @cfg {string} after - input group add on after
10725  * @cfg {string} size - (lg|sm) or leave empty..
10726  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10727  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10728  * @cfg {Number} md colspan out of 12 for computer-sized screens
10729  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10730  * @cfg {string} value default value of the input
10731  * @cfg {Number} labelWidth set the width of label 
10732  * @cfg {Number} labellg set the width of label (1-12)
10733  * @cfg {Number} labelmd set the width of label (1-12)
10734  * @cfg {Number} labelsm set the width of label (1-12)
10735  * @cfg {Number} labelxs set the width of label (1-12)
10736  * @cfg {String} labelAlign (top|left)
10737  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10738  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10739  * @cfg {String} indicatorpos (left|right) default left
10740  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10741  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10742  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10743
10744  * @cfg {String} align (left|center|right) Default left
10745  * @cfg {Boolean} forceFeedback (true|false) Default false
10746  * 
10747  * @constructor
10748  * Create a new Input
10749  * @param {Object} config The config object
10750  */
10751
10752 Roo.bootstrap.Input = function(config){
10753     
10754     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10755     
10756     this.addEvents({
10757         /**
10758          * @event focus
10759          * Fires when this field receives input focus.
10760          * @param {Roo.form.Field} this
10761          */
10762         focus : true,
10763         /**
10764          * @event blur
10765          * Fires when this field loses input focus.
10766          * @param {Roo.form.Field} this
10767          */
10768         blur : true,
10769         /**
10770          * @event specialkey
10771          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10772          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10773          * @param {Roo.form.Field} this
10774          * @param {Roo.EventObject} e The event object
10775          */
10776         specialkey : true,
10777         /**
10778          * @event change
10779          * Fires just before the field blurs if the field value has changed.
10780          * @param {Roo.form.Field} this
10781          * @param {Mixed} newValue The new value
10782          * @param {Mixed} oldValue The original value
10783          */
10784         change : true,
10785         /**
10786          * @event invalid
10787          * Fires after the field has been marked as invalid.
10788          * @param {Roo.form.Field} this
10789          * @param {String} msg The validation message
10790          */
10791         invalid : true,
10792         /**
10793          * @event valid
10794          * Fires after the field has been validated with no errors.
10795          * @param {Roo.form.Field} this
10796          */
10797         valid : true,
10798          /**
10799          * @event keyup
10800          * Fires after the key up
10801          * @param {Roo.form.Field} this
10802          * @param {Roo.EventObject}  e The event Object
10803          */
10804         keyup : true,
10805         /**
10806          * @event paste
10807          * Fires after the user pastes into input
10808          * @param {Roo.form.Field} this
10809          * @param {Roo.EventObject}  e The event Object
10810          */
10811         paste : true
10812     });
10813 };
10814
10815 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10816      /**
10817      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10818       automatic validation (defaults to "keyup").
10819      */
10820     validationEvent : "keyup",
10821      /**
10822      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10823      */
10824     validateOnBlur : true,
10825     /**
10826      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10827      */
10828     validationDelay : 250,
10829      /**
10830      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10831      */
10832     focusClass : "x-form-focus",  // not needed???
10833     
10834        
10835     /**
10836      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10837      */
10838     invalidClass : "has-warning",
10839     
10840     /**
10841      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10842      */
10843     validClass : "has-success",
10844     
10845     /**
10846      * @cfg {Boolean} hasFeedback (true|false) default true
10847      */
10848     hasFeedback : true,
10849     
10850     /**
10851      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10852      */
10853     invalidFeedbackClass : "glyphicon-warning-sign",
10854     
10855     /**
10856      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10857      */
10858     validFeedbackClass : "glyphicon-ok",
10859     
10860     /**
10861      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10862      */
10863     selectOnFocus : false,
10864     
10865      /**
10866      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10867      */
10868     maskRe : null,
10869        /**
10870      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10871      */
10872     vtype : null,
10873     
10874       /**
10875      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10876      */
10877     disableKeyFilter : false,
10878     
10879        /**
10880      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10881      */
10882     disabled : false,
10883      /**
10884      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10885      */
10886     allowBlank : true,
10887     /**
10888      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10889      */
10890     blankText : "Please complete this mandatory field",
10891     
10892      /**
10893      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10894      */
10895     minLength : 0,
10896     /**
10897      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10898      */
10899     maxLength : Number.MAX_VALUE,
10900     /**
10901      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10902      */
10903     minLengthText : "The minimum length for this field is {0}",
10904     /**
10905      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10906      */
10907     maxLengthText : "The maximum length for this field is {0}",
10908   
10909     
10910     /**
10911      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10912      * If available, this function will be called only after the basic validators all return true, and will be passed the
10913      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10914      */
10915     validator : null,
10916     /**
10917      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10918      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10919      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10920      */
10921     regex : null,
10922     /**
10923      * @cfg {String} regexText -- Depricated - use Invalid Text
10924      */
10925     regexText : "",
10926     
10927     /**
10928      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10929      */
10930     invalidText : "",
10931     
10932     
10933     
10934     autocomplete: false,
10935     
10936     
10937     fieldLabel : '',
10938     inputType : 'text',
10939     
10940     name : false,
10941     placeholder: false,
10942     before : false,
10943     after : false,
10944     size : false,
10945     hasFocus : false,
10946     preventMark: false,
10947     isFormField : true,
10948     value : '',
10949     labelWidth : 2,
10950     labelAlign : false,
10951     readOnly : false,
10952     align : false,
10953     formatedValue : false,
10954     forceFeedback : false,
10955     
10956     indicatorpos : 'left',
10957     
10958     labellg : 0,
10959     labelmd : 0,
10960     labelsm : 0,
10961     labelxs : 0,
10962     
10963     capture : '',
10964     accept : '',
10965     
10966     parentLabelAlign : function()
10967     {
10968         var parent = this;
10969         while (parent.parent()) {
10970             parent = parent.parent();
10971             if (typeof(parent.labelAlign) !='undefined') {
10972                 return parent.labelAlign;
10973             }
10974         }
10975         return 'left';
10976         
10977     },
10978     
10979     getAutoCreate : function()
10980     {
10981         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10982         
10983         var id = Roo.id();
10984         
10985         var cfg = {};
10986         
10987         if(this.inputType != 'hidden'){
10988             cfg.cls = 'form-group' //input-group
10989         }
10990         
10991         var input =  {
10992             tag: 'input',
10993             id : id,
10994             type : this.inputType,
10995             value : this.value,
10996             cls : 'form-control',
10997             placeholder : this.placeholder || '',
10998             autocomplete : this.autocomplete || 'new-password'
10999         };
11000         if (this.inputType == 'file') {
11001             input.style = 'overflow:hidden'; // why not in CSS?
11002         }
11003         
11004         if(this.capture.length){
11005             input.capture = this.capture;
11006         }
11007         
11008         if(this.accept.length){
11009             input.accept = this.accept + "/*";
11010         }
11011         
11012         if(this.align){
11013             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11014         }
11015         
11016         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11017             input.maxLength = this.maxLength;
11018         }
11019         
11020         if (this.disabled) {
11021             input.disabled=true;
11022         }
11023         
11024         if (this.readOnly) {
11025             input.readonly=true;
11026         }
11027         
11028         if (this.name) {
11029             input.name = this.name;
11030         }
11031         
11032         if (this.size) {
11033             input.cls += ' input-' + this.size;
11034         }
11035         
11036         var settings=this;
11037         ['xs','sm','md','lg'].map(function(size){
11038             if (settings[size]) {
11039                 cfg.cls += ' col-' + size + '-' + settings[size];
11040             }
11041         });
11042         
11043         var inputblock = input;
11044         
11045         var feedback = {
11046             tag: 'span',
11047             cls: 'glyphicon form-control-feedback'
11048         };
11049             
11050         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11051             
11052             inputblock = {
11053                 cls : 'has-feedback',
11054                 cn :  [
11055                     input,
11056                     feedback
11057                 ] 
11058             };  
11059         }
11060         
11061         if (this.before || this.after) {
11062             
11063             inputblock = {
11064                 cls : 'input-group',
11065                 cn :  [] 
11066             };
11067             
11068             if (this.before && typeof(this.before) == 'string') {
11069                 
11070                 inputblock.cn.push({
11071                     tag :'span',
11072                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11073                     html : this.before
11074                 });
11075             }
11076             if (this.before && typeof(this.before) == 'object') {
11077                 this.before = Roo.factory(this.before);
11078                 
11079                 inputblock.cn.push({
11080                     tag :'span',
11081                     cls : 'roo-input-before input-group-prepend   input-group-' +
11082                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11083                 });
11084             }
11085             
11086             inputblock.cn.push(input);
11087             
11088             if (this.after && typeof(this.after) == 'string') {
11089                 inputblock.cn.push({
11090                     tag :'span',
11091                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11092                     html : this.after
11093                 });
11094             }
11095             if (this.after && typeof(this.after) == 'object') {
11096                 this.after = Roo.factory(this.after);
11097                 
11098                 inputblock.cn.push({
11099                     tag :'span',
11100                     cls : 'roo-input-after input-group-append  input-group-' +
11101                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11102                 });
11103             }
11104             
11105             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11106                 inputblock.cls += ' has-feedback';
11107                 inputblock.cn.push(feedback);
11108             }
11109         };
11110         var indicator = {
11111             tag : 'i',
11112             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11113             tooltip : 'This field is required'
11114         };
11115         if (this.allowBlank ) {
11116             indicator.style = this.allowBlank ? ' display:none' : '';
11117         }
11118         if (align ==='left' && this.fieldLabel.length) {
11119             
11120             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11121             
11122             cfg.cn = [
11123                 indicator,
11124                 {
11125                     tag: 'label',
11126                     'for' :  id,
11127                     cls : 'control-label col-form-label',
11128                     html : this.fieldLabel
11129
11130                 },
11131                 {
11132                     cls : "", 
11133                     cn: [
11134                         inputblock
11135                     ]
11136                 }
11137             ];
11138             
11139             var labelCfg = cfg.cn[1];
11140             var contentCfg = cfg.cn[2];
11141             
11142             if(this.indicatorpos == 'right'){
11143                 cfg.cn = [
11144                     {
11145                         tag: 'label',
11146                         'for' :  id,
11147                         cls : 'control-label col-form-label',
11148                         cn : [
11149                             {
11150                                 tag : 'span',
11151                                 html : this.fieldLabel
11152                             },
11153                             indicator
11154                         ]
11155                     },
11156                     {
11157                         cls : "",
11158                         cn: [
11159                             inputblock
11160                         ]
11161                     }
11162
11163                 ];
11164                 
11165                 labelCfg = cfg.cn[0];
11166                 contentCfg = cfg.cn[1];
11167             
11168             }
11169             
11170             if(this.labelWidth > 12){
11171                 labelCfg.style = "width: " + this.labelWidth + 'px';
11172             }
11173             
11174             if(this.labelWidth < 13 && this.labelmd == 0){
11175                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11176             }
11177             
11178             if(this.labellg > 0){
11179                 labelCfg.cls += ' col-lg-' + this.labellg;
11180                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11181             }
11182             
11183             if(this.labelmd > 0){
11184                 labelCfg.cls += ' col-md-' + this.labelmd;
11185                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11186             }
11187             
11188             if(this.labelsm > 0){
11189                 labelCfg.cls += ' col-sm-' + this.labelsm;
11190                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11191             }
11192             
11193             if(this.labelxs > 0){
11194                 labelCfg.cls += ' col-xs-' + this.labelxs;
11195                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11196             }
11197             
11198             
11199         } else if ( this.fieldLabel.length) {
11200                 
11201             
11202             
11203             cfg.cn = [
11204                 {
11205                     tag : 'i',
11206                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11207                     tooltip : 'This field is required',
11208                     style : this.allowBlank ? ' display:none' : '' 
11209                 },
11210                 {
11211                     tag: 'label',
11212                    //cls : 'input-group-addon',
11213                     html : this.fieldLabel
11214
11215                 },
11216
11217                inputblock
11218
11219            ];
11220            
11221            if(this.indicatorpos == 'right'){
11222        
11223                 cfg.cn = [
11224                     {
11225                         tag: 'label',
11226                        //cls : 'input-group-addon',
11227                         html : this.fieldLabel
11228
11229                     },
11230                     {
11231                         tag : 'i',
11232                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11233                         tooltip : 'This field is required',
11234                         style : this.allowBlank ? ' display:none' : '' 
11235                     },
11236
11237                    inputblock
11238
11239                ];
11240
11241             }
11242
11243         } else {
11244             
11245             cfg.cn = [
11246
11247                     inputblock
11248
11249             ];
11250                 
11251                 
11252         };
11253         
11254         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11255            cfg.cls += ' navbar-form';
11256         }
11257         
11258         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11259             // on BS4 we do this only if not form 
11260             cfg.cls += ' navbar-form';
11261             cfg.tag = 'li';
11262         }
11263         
11264         return cfg;
11265         
11266     },
11267     /**
11268      * return the real input element.
11269      */
11270     inputEl: function ()
11271     {
11272         return this.el.select('input.form-control',true).first();
11273     },
11274     
11275     tooltipEl : function()
11276     {
11277         return this.inputEl();
11278     },
11279     
11280     indicatorEl : function()
11281     {
11282         if (Roo.bootstrap.version == 4) {
11283             return false; // not enabled in v4 yet.
11284         }
11285         
11286         var indicator = this.el.select('i.roo-required-indicator',true).first();
11287         
11288         if(!indicator){
11289             return false;
11290         }
11291         
11292         return indicator;
11293         
11294     },
11295     
11296     setDisabled : function(v)
11297     {
11298         var i  = this.inputEl().dom;
11299         if (!v) {
11300             i.removeAttribute('disabled');
11301             return;
11302             
11303         }
11304         i.setAttribute('disabled','true');
11305     },
11306     initEvents : function()
11307     {
11308           
11309         this.inputEl().on("keydown" , this.fireKey,  this);
11310         this.inputEl().on("focus", this.onFocus,  this);
11311         this.inputEl().on("blur", this.onBlur,  this);
11312         
11313         this.inputEl().relayEvent('keyup', this);
11314         this.inputEl().relayEvent('paste', this);
11315         
11316         this.indicator = this.indicatorEl();
11317         
11318         if(this.indicator){
11319             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11320         }
11321  
11322         // reference to original value for reset
11323         this.originalValue = this.getValue();
11324         //Roo.form.TextField.superclass.initEvents.call(this);
11325         if(this.validationEvent == 'keyup'){
11326             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11327             this.inputEl().on('keyup', this.filterValidation, this);
11328         }
11329         else if(this.validationEvent !== false){
11330             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11331         }
11332         
11333         if(this.selectOnFocus){
11334             this.on("focus", this.preFocus, this);
11335             
11336         }
11337         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11338             this.inputEl().on("keypress", this.filterKeys, this);
11339         } else {
11340             this.inputEl().relayEvent('keypress', this);
11341         }
11342        /* if(this.grow){
11343             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11344             this.el.on("click", this.autoSize,  this);
11345         }
11346         */
11347         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11348             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11349         }
11350         
11351         if (typeof(this.before) == 'object') {
11352             this.before.render(this.el.select('.roo-input-before',true).first());
11353         }
11354         if (typeof(this.after) == 'object') {
11355             this.after.render(this.el.select('.roo-input-after',true).first());
11356         }
11357         
11358         this.inputEl().on('change', this.onChange, this);
11359         
11360     },
11361     filterValidation : function(e){
11362         if(!e.isNavKeyPress()){
11363             this.validationTask.delay(this.validationDelay);
11364         }
11365     },
11366      /**
11367      * Validates the field value
11368      * @return {Boolean} True if the value is valid, else false
11369      */
11370     validate : function(){
11371         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11372         if(this.disabled || this.validateValue(this.getRawValue())){
11373             this.markValid();
11374             return true;
11375         }
11376         
11377         this.markInvalid();
11378         return false;
11379     },
11380     
11381     
11382     /**
11383      * Validates a value according to the field's validation rules and marks the field as invalid
11384      * if the validation fails
11385      * @param {Mixed} value The value to validate
11386      * @return {Boolean} True if the value is valid, else false
11387      */
11388     validateValue : function(value)
11389     {
11390         if(this.getVisibilityEl().hasClass('hidden')){
11391             return true;
11392         }
11393         
11394         if(value.length < 1)  { // if it's blank
11395             if(this.allowBlank){
11396                 return true;
11397             }
11398             return false;
11399         }
11400         
11401         if(value.length < this.minLength){
11402             return false;
11403         }
11404         if(value.length > this.maxLength){
11405             return false;
11406         }
11407         if(this.vtype){
11408             var vt = Roo.form.VTypes;
11409             if(!vt[this.vtype](value, this)){
11410                 return false;
11411             }
11412         }
11413         if(typeof this.validator == "function"){
11414             var msg = this.validator(value);
11415             if(msg !== true){
11416                 return false;
11417             }
11418             if (typeof(msg) == 'string') {
11419                 this.invalidText = msg;
11420             }
11421         }
11422         
11423         if(this.regex && !this.regex.test(value)){
11424             return false;
11425         }
11426         
11427         return true;
11428     },
11429     
11430      // private
11431     fireKey : function(e){
11432         //Roo.log('field ' + e.getKey());
11433         if(e.isNavKeyPress()){
11434             this.fireEvent("specialkey", this, e);
11435         }
11436     },
11437     focus : function (selectText){
11438         if(this.rendered){
11439             this.inputEl().focus();
11440             if(selectText === true){
11441                 this.inputEl().dom.select();
11442             }
11443         }
11444         return this;
11445     } ,
11446     
11447     onFocus : function(){
11448         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11449            // this.el.addClass(this.focusClass);
11450         }
11451         if(!this.hasFocus){
11452             this.hasFocus = true;
11453             this.startValue = this.getValue();
11454             this.fireEvent("focus", this);
11455         }
11456     },
11457     
11458     beforeBlur : Roo.emptyFn,
11459
11460     
11461     // private
11462     onBlur : function(){
11463         this.beforeBlur();
11464         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11465             //this.el.removeClass(this.focusClass);
11466         }
11467         this.hasFocus = false;
11468         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11469             this.validate();
11470         }
11471         var v = this.getValue();
11472         if(String(v) !== String(this.startValue)){
11473             this.fireEvent('change', this, v, this.startValue);
11474         }
11475         this.fireEvent("blur", this);
11476     },
11477     
11478     onChange : function(e)
11479     {
11480         var v = this.getValue();
11481         if(String(v) !== String(this.startValue)){
11482             this.fireEvent('change', this, v, this.startValue);
11483         }
11484         
11485     },
11486     
11487     /**
11488      * Resets the current field value to the originally loaded value and clears any validation messages
11489      */
11490     reset : function(){
11491         this.setValue(this.originalValue);
11492         this.validate();
11493     },
11494      /**
11495      * Returns the name of the field
11496      * @return {Mixed} name The name field
11497      */
11498     getName: function(){
11499         return this.name;
11500     },
11501      /**
11502      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11503      * @return {Mixed} value The field value
11504      */
11505     getValue : function(){
11506         
11507         var v = this.inputEl().getValue();
11508         
11509         return v;
11510     },
11511     /**
11512      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11513      * @return {Mixed} value The field value
11514      */
11515     getRawValue : function(){
11516         var v = this.inputEl().getValue();
11517         
11518         return v;
11519     },
11520     
11521     /**
11522      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11523      * @param {Mixed} value The value to set
11524      */
11525     setRawValue : function(v){
11526         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11527     },
11528     
11529     selectText : function(start, end){
11530         var v = this.getRawValue();
11531         if(v.length > 0){
11532             start = start === undefined ? 0 : start;
11533             end = end === undefined ? v.length : end;
11534             var d = this.inputEl().dom;
11535             if(d.setSelectionRange){
11536                 d.setSelectionRange(start, end);
11537             }else if(d.createTextRange){
11538                 var range = d.createTextRange();
11539                 range.moveStart("character", start);
11540                 range.moveEnd("character", v.length-end);
11541                 range.select();
11542             }
11543         }
11544     },
11545     
11546     /**
11547      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11548      * @param {Mixed} value The value to set
11549      */
11550     setValue : function(v){
11551         this.value = v;
11552         if(this.rendered){
11553             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11554             this.validate();
11555         }
11556     },
11557     
11558     /*
11559     processValue : function(value){
11560         if(this.stripCharsRe){
11561             var newValue = value.replace(this.stripCharsRe, '');
11562             if(newValue !== value){
11563                 this.setRawValue(newValue);
11564                 return newValue;
11565             }
11566         }
11567         return value;
11568     },
11569   */
11570     preFocus : function(){
11571         
11572         if(this.selectOnFocus){
11573             this.inputEl().dom.select();
11574         }
11575     },
11576     filterKeys : function(e){
11577         var k = e.getKey();
11578         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11579             return;
11580         }
11581         var c = e.getCharCode(), cc = String.fromCharCode(c);
11582         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11583             return;
11584         }
11585         if(!this.maskRe.test(cc)){
11586             e.stopEvent();
11587         }
11588     },
11589      /**
11590      * Clear any invalid styles/messages for this field
11591      */
11592     clearInvalid : function(){
11593         
11594         if(!this.el || this.preventMark){ // not rendered
11595             return;
11596         }
11597         
11598         
11599         this.el.removeClass([this.invalidClass, 'is-invalid']);
11600         
11601         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11602             
11603             var feedback = this.el.select('.form-control-feedback', true).first();
11604             
11605             if(feedback){
11606                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11607             }
11608             
11609         }
11610         
11611         if(this.indicator){
11612             this.indicator.removeClass('visible');
11613             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11614         }
11615         
11616         this.fireEvent('valid', this);
11617     },
11618     
11619      /**
11620      * Mark this field as valid
11621      */
11622     markValid : function()
11623     {
11624         if(!this.el  || this.preventMark){ // not rendered...
11625             return;
11626         }
11627         
11628         this.el.removeClass([this.invalidClass, this.validClass]);
11629         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11630
11631         var feedback = this.el.select('.form-control-feedback', true).first();
11632             
11633         if(feedback){
11634             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11635         }
11636         
11637         if(this.indicator){
11638             this.indicator.removeClass('visible');
11639             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11640         }
11641         
11642         if(this.disabled){
11643             return;
11644         }
11645         
11646            
11647         if(this.allowBlank && !this.getRawValue().length){
11648             return;
11649         }
11650         if (Roo.bootstrap.version == 3) {
11651             this.el.addClass(this.validClass);
11652         } else {
11653             this.inputEl().addClass('is-valid');
11654         }
11655
11656         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11657             
11658             var feedback = this.el.select('.form-control-feedback', true).first();
11659             
11660             if(feedback){
11661                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11662                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11663             }
11664             
11665         }
11666         
11667         this.fireEvent('valid', this);
11668     },
11669     
11670      /**
11671      * Mark this field as invalid
11672      * @param {String} msg The validation message
11673      */
11674     markInvalid : function(msg)
11675     {
11676         if(!this.el  || this.preventMark){ // not rendered
11677             return;
11678         }
11679         
11680         this.el.removeClass([this.invalidClass, this.validClass]);
11681         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11682         
11683         var feedback = this.el.select('.form-control-feedback', true).first();
11684             
11685         if(feedback){
11686             this.el.select('.form-control-feedback', true).first().removeClass(
11687                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11688         }
11689
11690         if(this.disabled){
11691             return;
11692         }
11693         
11694         if(this.allowBlank && !this.getRawValue().length){
11695             return;
11696         }
11697         
11698         if(this.indicator){
11699             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11700             this.indicator.addClass('visible');
11701         }
11702         if (Roo.bootstrap.version == 3) {
11703             this.el.addClass(this.invalidClass);
11704         } else {
11705             this.inputEl().addClass('is-invalid');
11706         }
11707         
11708         
11709         
11710         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11711             
11712             var feedback = this.el.select('.form-control-feedback', true).first();
11713             
11714             if(feedback){
11715                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11716                 
11717                 if(this.getValue().length || this.forceFeedback){
11718                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11719                 }
11720                 
11721             }
11722             
11723         }
11724         
11725         this.fireEvent('invalid', this, msg);
11726     },
11727     // private
11728     SafariOnKeyDown : function(event)
11729     {
11730         // this is a workaround for a password hang bug on chrome/ webkit.
11731         if (this.inputEl().dom.type != 'password') {
11732             return;
11733         }
11734         
11735         var isSelectAll = false;
11736         
11737         if(this.inputEl().dom.selectionEnd > 0){
11738             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11739         }
11740         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11741             event.preventDefault();
11742             this.setValue('');
11743             return;
11744         }
11745         
11746         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11747             
11748             event.preventDefault();
11749             // this is very hacky as keydown always get's upper case.
11750             //
11751             var cc = String.fromCharCode(event.getCharCode());
11752             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11753             
11754         }
11755     },
11756     adjustWidth : function(tag, w){
11757         tag = tag.toLowerCase();
11758         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11759             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11760                 if(tag == 'input'){
11761                     return w + 2;
11762                 }
11763                 if(tag == 'textarea'){
11764                     return w-2;
11765                 }
11766             }else if(Roo.isOpera){
11767                 if(tag == 'input'){
11768                     return w + 2;
11769                 }
11770                 if(tag == 'textarea'){
11771                     return w-2;
11772                 }
11773             }
11774         }
11775         return w;
11776     },
11777     
11778     setFieldLabel : function(v)
11779     {
11780         if(!this.rendered){
11781             return;
11782         }
11783         
11784         if(this.indicatorEl()){
11785             var ar = this.el.select('label > span',true);
11786             
11787             if (ar.elements.length) {
11788                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11789                 this.fieldLabel = v;
11790                 return;
11791             }
11792             
11793             var br = this.el.select('label',true);
11794             
11795             if(br.elements.length) {
11796                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11797                 this.fieldLabel = v;
11798                 return;
11799             }
11800             
11801             Roo.log('Cannot Found any of label > span || label in input');
11802             return;
11803         }
11804         
11805         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11806         this.fieldLabel = v;
11807         
11808         
11809     }
11810 });
11811
11812  
11813 /*
11814  * - LGPL
11815  *
11816  * Input
11817  * 
11818  */
11819
11820 /**
11821  * @class Roo.bootstrap.TextArea
11822  * @extends Roo.bootstrap.Input
11823  * Bootstrap TextArea class
11824  * @cfg {Number} cols Specifies the visible width of a text area
11825  * @cfg {Number} rows Specifies the visible number of lines in a text area
11826  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11827  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11828  * @cfg {string} html text
11829  * 
11830  * @constructor
11831  * Create a new TextArea
11832  * @param {Object} config The config object
11833  */
11834
11835 Roo.bootstrap.TextArea = function(config){
11836     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11837    
11838 };
11839
11840 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11841      
11842     cols : false,
11843     rows : 5,
11844     readOnly : false,
11845     warp : 'soft',
11846     resize : false,
11847     value: false,
11848     html: false,
11849     
11850     getAutoCreate : function(){
11851         
11852         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11853         
11854         var id = Roo.id();
11855         
11856         var cfg = {};
11857         
11858         if(this.inputType != 'hidden'){
11859             cfg.cls = 'form-group' //input-group
11860         }
11861         
11862         var input =  {
11863             tag: 'textarea',
11864             id : id,
11865             warp : this.warp,
11866             rows : this.rows,
11867             value : this.value || '',
11868             html: this.html || '',
11869             cls : 'form-control',
11870             placeholder : this.placeholder || '' 
11871             
11872         };
11873         
11874         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11875             input.maxLength = this.maxLength;
11876         }
11877         
11878         if(this.resize){
11879             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11880         }
11881         
11882         if(this.cols){
11883             input.cols = this.cols;
11884         }
11885         
11886         if (this.readOnly) {
11887             input.readonly = true;
11888         }
11889         
11890         if (this.name) {
11891             input.name = this.name;
11892         }
11893         
11894         if (this.size) {
11895             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11896         }
11897         
11898         var settings=this;
11899         ['xs','sm','md','lg'].map(function(size){
11900             if (settings[size]) {
11901                 cfg.cls += ' col-' + size + '-' + settings[size];
11902             }
11903         });
11904         
11905         var inputblock = input;
11906         
11907         if(this.hasFeedback && !this.allowBlank){
11908             
11909             var feedback = {
11910                 tag: 'span',
11911                 cls: 'glyphicon form-control-feedback'
11912             };
11913
11914             inputblock = {
11915                 cls : 'has-feedback',
11916                 cn :  [
11917                     input,
11918                     feedback
11919                 ] 
11920             };  
11921         }
11922         
11923         
11924         if (this.before || this.after) {
11925             
11926             inputblock = {
11927                 cls : 'input-group',
11928                 cn :  [] 
11929             };
11930             if (this.before) {
11931                 inputblock.cn.push({
11932                     tag :'span',
11933                     cls : 'input-group-addon',
11934                     html : this.before
11935                 });
11936             }
11937             
11938             inputblock.cn.push(input);
11939             
11940             if(this.hasFeedback && !this.allowBlank){
11941                 inputblock.cls += ' has-feedback';
11942                 inputblock.cn.push(feedback);
11943             }
11944             
11945             if (this.after) {
11946                 inputblock.cn.push({
11947                     tag :'span',
11948                     cls : 'input-group-addon',
11949                     html : this.after
11950                 });
11951             }
11952             
11953         }
11954         
11955         if (align ==='left' && this.fieldLabel.length) {
11956             cfg.cn = [
11957                 {
11958                     tag: 'label',
11959                     'for' :  id,
11960                     cls : 'control-label',
11961                     html : this.fieldLabel
11962                 },
11963                 {
11964                     cls : "",
11965                     cn: [
11966                         inputblock
11967                     ]
11968                 }
11969
11970             ];
11971             
11972             if(this.labelWidth > 12){
11973                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11974             }
11975
11976             if(this.labelWidth < 13 && this.labelmd == 0){
11977                 this.labelmd = this.labelWidth;
11978             }
11979
11980             if(this.labellg > 0){
11981                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11982                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11983             }
11984
11985             if(this.labelmd > 0){
11986                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11987                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11988             }
11989
11990             if(this.labelsm > 0){
11991                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11992                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11993             }
11994
11995             if(this.labelxs > 0){
11996                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11997                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11998             }
11999             
12000         } else if ( this.fieldLabel.length) {
12001             cfg.cn = [
12002
12003                {
12004                    tag: 'label',
12005                    //cls : 'input-group-addon',
12006                    html : this.fieldLabel
12007
12008                },
12009
12010                inputblock
12011
12012            ];
12013
12014         } else {
12015
12016             cfg.cn = [
12017
12018                 inputblock
12019
12020             ];
12021                 
12022         }
12023         
12024         if (this.disabled) {
12025             input.disabled=true;
12026         }
12027         
12028         return cfg;
12029         
12030     },
12031     /**
12032      * return the real textarea element.
12033      */
12034     inputEl: function ()
12035     {
12036         return this.el.select('textarea.form-control',true).first();
12037     },
12038     
12039     /**
12040      * Clear any invalid styles/messages for this field
12041      */
12042     clearInvalid : function()
12043     {
12044         
12045         if(!this.el || this.preventMark){ // not rendered
12046             return;
12047         }
12048         
12049         var label = this.el.select('label', true).first();
12050         var icon = this.el.select('i.fa-star', true).first();
12051         
12052         if(label && icon){
12053             icon.remove();
12054         }
12055         this.el.removeClass( this.validClass);
12056         this.inputEl().removeClass('is-invalid');
12057          
12058         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12059             
12060             var feedback = this.el.select('.form-control-feedback', true).first();
12061             
12062             if(feedback){
12063                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12064             }
12065             
12066         }
12067         
12068         this.fireEvent('valid', this);
12069     },
12070     
12071      /**
12072      * Mark this field as valid
12073      */
12074     markValid : function()
12075     {
12076         if(!this.el  || this.preventMark){ // not rendered
12077             return;
12078         }
12079         
12080         this.el.removeClass([this.invalidClass, this.validClass]);
12081         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12082         
12083         var feedback = this.el.select('.form-control-feedback', true).first();
12084             
12085         if(feedback){
12086             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12087         }
12088
12089         if(this.disabled || this.allowBlank){
12090             return;
12091         }
12092         
12093         var label = this.el.select('label', true).first();
12094         var icon = this.el.select('i.fa-star', true).first();
12095         
12096         if(label && icon){
12097             icon.remove();
12098         }
12099         if (Roo.bootstrap.version == 3) {
12100             this.el.addClass(this.validClass);
12101         } else {
12102             this.inputEl().addClass('is-valid');
12103         }
12104         
12105         
12106         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12107             
12108             var feedback = this.el.select('.form-control-feedback', true).first();
12109             
12110             if(feedback){
12111                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12112                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12113             }
12114             
12115         }
12116         
12117         this.fireEvent('valid', this);
12118     },
12119     
12120      /**
12121      * Mark this field as invalid
12122      * @param {String} msg The validation message
12123      */
12124     markInvalid : function(msg)
12125     {
12126         if(!this.el  || this.preventMark){ // not rendered
12127             return;
12128         }
12129         
12130         this.el.removeClass([this.invalidClass, this.validClass]);
12131         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12132         
12133         var feedback = this.el.select('.form-control-feedback', true).first();
12134             
12135         if(feedback){
12136             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12137         }
12138
12139         if(this.disabled || this.allowBlank){
12140             return;
12141         }
12142         
12143         var label = this.el.select('label', true).first();
12144         var icon = this.el.select('i.fa-star', true).first();
12145         
12146         if(!this.getValue().length && label && !icon){
12147             this.el.createChild({
12148                 tag : 'i',
12149                 cls : 'text-danger fa fa-lg fa-star',
12150                 tooltip : 'This field is required',
12151                 style : 'margin-right:5px;'
12152             }, label, true);
12153         }
12154         
12155         if (Roo.bootstrap.version == 3) {
12156             this.el.addClass(this.invalidClass);
12157         } else {
12158             this.inputEl().addClass('is-invalid');
12159         }
12160         
12161         // fixme ... this may be depricated need to test..
12162         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12163             
12164             var feedback = this.el.select('.form-control-feedback', true).first();
12165             
12166             if(feedback){
12167                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12168                 
12169                 if(this.getValue().length || this.forceFeedback){
12170                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12171                 }
12172                 
12173             }
12174             
12175         }
12176         
12177         this.fireEvent('invalid', this, msg);
12178     }
12179 });
12180
12181  
12182 /*
12183  * - LGPL
12184  *
12185  * trigger field - base class for combo..
12186  * 
12187  */
12188  
12189 /**
12190  * @class Roo.bootstrap.TriggerField
12191  * @extends Roo.bootstrap.Input
12192  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12193  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12194  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12195  * for which you can provide a custom implementation.  For example:
12196  * <pre><code>
12197 var trigger = new Roo.bootstrap.TriggerField();
12198 trigger.onTriggerClick = myTriggerFn;
12199 trigger.applyTo('my-field');
12200 </code></pre>
12201  *
12202  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12203  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12204  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12205  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12206  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12207
12208  * @constructor
12209  * Create a new TriggerField.
12210  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12211  * to the base TextField)
12212  */
12213 Roo.bootstrap.TriggerField = function(config){
12214     this.mimicing = false;
12215     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12216 };
12217
12218 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12219     /**
12220      * @cfg {String} triggerClass A CSS class to apply to the trigger
12221      */
12222      /**
12223      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12224      */
12225     hideTrigger:false,
12226
12227     /**
12228      * @cfg {Boolean} removable (true|false) special filter default false
12229      */
12230     removable : false,
12231     
12232     /** @cfg {Boolean} grow @hide */
12233     /** @cfg {Number} growMin @hide */
12234     /** @cfg {Number} growMax @hide */
12235
12236     /**
12237      * @hide 
12238      * @method
12239      */
12240     autoSize: Roo.emptyFn,
12241     // private
12242     monitorTab : true,
12243     // private
12244     deferHeight : true,
12245
12246     
12247     actionMode : 'wrap',
12248     
12249     caret : false,
12250     
12251     
12252     getAutoCreate : function(){
12253        
12254         var align = this.labelAlign || this.parentLabelAlign();
12255         
12256         var id = Roo.id();
12257         
12258         var cfg = {
12259             cls: 'form-group' //input-group
12260         };
12261         
12262         
12263         var input =  {
12264             tag: 'input',
12265             id : id,
12266             type : this.inputType,
12267             cls : 'form-control',
12268             autocomplete: 'new-password',
12269             placeholder : this.placeholder || '' 
12270             
12271         };
12272         if (this.name) {
12273             input.name = this.name;
12274         }
12275         if (this.size) {
12276             input.cls += ' input-' + this.size;
12277         }
12278         
12279         if (this.disabled) {
12280             input.disabled=true;
12281         }
12282         
12283         var inputblock = input;
12284         
12285         if(this.hasFeedback && !this.allowBlank){
12286             
12287             var feedback = {
12288                 tag: 'span',
12289                 cls: 'glyphicon form-control-feedback'
12290             };
12291             
12292             if(this.removable && !this.editable  ){
12293                 inputblock = {
12294                     cls : 'has-feedback',
12295                     cn :  [
12296                         inputblock,
12297                         {
12298                             tag: 'button',
12299                             html : 'x',
12300                             cls : 'roo-combo-removable-btn close'
12301                         },
12302                         feedback
12303                     ] 
12304                 };
12305             } else {
12306                 inputblock = {
12307                     cls : 'has-feedback',
12308                     cn :  [
12309                         inputblock,
12310                         feedback
12311                     ] 
12312                 };
12313             }
12314
12315         } else {
12316             if(this.removable && !this.editable ){
12317                 inputblock = {
12318                     cls : 'roo-removable',
12319                     cn :  [
12320                         inputblock,
12321                         {
12322                             tag: 'button',
12323                             html : 'x',
12324                             cls : 'roo-combo-removable-btn close'
12325                         }
12326                     ] 
12327                 };
12328             }
12329         }
12330         
12331         if (this.before || this.after) {
12332             
12333             inputblock = {
12334                 cls : 'input-group',
12335                 cn :  [] 
12336             };
12337             if (this.before) {
12338                 inputblock.cn.push({
12339                     tag :'span',
12340                     cls : 'input-group-addon input-group-prepend input-group-text',
12341                     html : this.before
12342                 });
12343             }
12344             
12345             inputblock.cn.push(input);
12346             
12347             if(this.hasFeedback && !this.allowBlank){
12348                 inputblock.cls += ' has-feedback';
12349                 inputblock.cn.push(feedback);
12350             }
12351             
12352             if (this.after) {
12353                 inputblock.cn.push({
12354                     tag :'span',
12355                     cls : 'input-group-addon input-group-append input-group-text',
12356                     html : this.after
12357                 });
12358             }
12359             
12360         };
12361         
12362       
12363         
12364         var ibwrap = inputblock;
12365         
12366         if(this.multiple){
12367             ibwrap = {
12368                 tag: 'ul',
12369                 cls: 'roo-select2-choices',
12370                 cn:[
12371                     {
12372                         tag: 'li',
12373                         cls: 'roo-select2-search-field',
12374                         cn: [
12375
12376                             inputblock
12377                         ]
12378                     }
12379                 ]
12380             };
12381                 
12382         }
12383         
12384         var combobox = {
12385             cls: 'roo-select2-container input-group',
12386             cn: [
12387                  {
12388                     tag: 'input',
12389                     type : 'hidden',
12390                     cls: 'form-hidden-field'
12391                 },
12392                 ibwrap
12393             ]
12394         };
12395         
12396         if(!this.multiple && this.showToggleBtn){
12397             
12398             var caret = {
12399                         tag: 'span',
12400                         cls: 'caret'
12401              };
12402             if (this.caret != false) {
12403                 caret = {
12404                      tag: 'i',
12405                      cls: 'fa fa-' + this.caret
12406                 };
12407                 
12408             }
12409             
12410             combobox.cn.push({
12411                 tag :'span',
12412                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12413                 cn : [
12414                     Roo.bootstrap.version == 3 ? caret : '',
12415                     {
12416                         tag: 'span',
12417                         cls: 'combobox-clear',
12418                         cn  : [
12419                             {
12420                                 tag : 'i',
12421                                 cls: 'icon-remove'
12422                             }
12423                         ]
12424                     }
12425                 ]
12426
12427             })
12428         }
12429         
12430         if(this.multiple){
12431             combobox.cls += ' roo-select2-container-multi';
12432         }
12433          var indicator = {
12434             tag : 'i',
12435             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12436             tooltip : 'This field is required'
12437         };
12438         if (Roo.bootstrap.version == 4) {
12439             indicator = {
12440                 tag : 'i',
12441                 style : 'display:none'
12442             };
12443         }
12444         
12445         
12446         if (align ==='left' && this.fieldLabel.length) {
12447             
12448             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12449
12450             cfg.cn = [
12451                 indicator,
12452                 {
12453                     tag: 'label',
12454                     'for' :  id,
12455                     cls : 'control-label',
12456                     html : this.fieldLabel
12457
12458                 },
12459                 {
12460                     cls : "", 
12461                     cn: [
12462                         combobox
12463                     ]
12464                 }
12465
12466             ];
12467             
12468             var labelCfg = cfg.cn[1];
12469             var contentCfg = cfg.cn[2];
12470             
12471             if(this.indicatorpos == 'right'){
12472                 cfg.cn = [
12473                     {
12474                         tag: 'label',
12475                         'for' :  id,
12476                         cls : 'control-label',
12477                         cn : [
12478                             {
12479                                 tag : 'span',
12480                                 html : this.fieldLabel
12481                             },
12482                             indicator
12483                         ]
12484                     },
12485                     {
12486                         cls : "", 
12487                         cn: [
12488                             combobox
12489                         ]
12490                     }
12491
12492                 ];
12493                 
12494                 labelCfg = cfg.cn[0];
12495                 contentCfg = cfg.cn[1];
12496             }
12497             
12498             if(this.labelWidth > 12){
12499                 labelCfg.style = "width: " + this.labelWidth + 'px';
12500             }
12501             
12502             if(this.labelWidth < 13 && this.labelmd == 0){
12503                 this.labelmd = this.labelWidth;
12504             }
12505             
12506             if(this.labellg > 0){
12507                 labelCfg.cls += ' col-lg-' + this.labellg;
12508                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12509             }
12510             
12511             if(this.labelmd > 0){
12512                 labelCfg.cls += ' col-md-' + this.labelmd;
12513                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12514             }
12515             
12516             if(this.labelsm > 0){
12517                 labelCfg.cls += ' col-sm-' + this.labelsm;
12518                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12519             }
12520             
12521             if(this.labelxs > 0){
12522                 labelCfg.cls += ' col-xs-' + this.labelxs;
12523                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12524             }
12525             
12526         } else if ( this.fieldLabel.length) {
12527 //                Roo.log(" label");
12528             cfg.cn = [
12529                 indicator,
12530                {
12531                    tag: 'label',
12532                    //cls : 'input-group-addon',
12533                    html : this.fieldLabel
12534
12535                },
12536
12537                combobox
12538
12539             ];
12540             
12541             if(this.indicatorpos == 'right'){
12542                 
12543                 cfg.cn = [
12544                     {
12545                        tag: 'label',
12546                        cn : [
12547                            {
12548                                tag : 'span',
12549                                html : this.fieldLabel
12550                            },
12551                            indicator
12552                        ]
12553
12554                     },
12555                     combobox
12556
12557                 ];
12558
12559             }
12560
12561         } else {
12562             
12563 //                Roo.log(" no label && no align");
12564                 cfg = combobox
12565                      
12566                 
12567         }
12568         
12569         var settings=this;
12570         ['xs','sm','md','lg'].map(function(size){
12571             if (settings[size]) {
12572                 cfg.cls += ' col-' + size + '-' + settings[size];
12573             }
12574         });
12575         
12576         return cfg;
12577         
12578     },
12579     
12580     
12581     
12582     // private
12583     onResize : function(w, h){
12584 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12585 //        if(typeof w == 'number'){
12586 //            var x = w - this.trigger.getWidth();
12587 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12588 //            this.trigger.setStyle('left', x+'px');
12589 //        }
12590     },
12591
12592     // private
12593     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12594
12595     // private
12596     getResizeEl : function(){
12597         return this.inputEl();
12598     },
12599
12600     // private
12601     getPositionEl : function(){
12602         return this.inputEl();
12603     },
12604
12605     // private
12606     alignErrorIcon : function(){
12607         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12608     },
12609
12610     // private
12611     initEvents : function(){
12612         
12613         this.createList();
12614         
12615         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12616         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12617         if(!this.multiple && this.showToggleBtn){
12618             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12619             if(this.hideTrigger){
12620                 this.trigger.setDisplayed(false);
12621             }
12622             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12623         }
12624         
12625         if(this.multiple){
12626             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12627         }
12628         
12629         if(this.removable && !this.editable && !this.tickable){
12630             var close = this.closeTriggerEl();
12631             
12632             if(close){
12633                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12634                 close.on('click', this.removeBtnClick, this, close);
12635             }
12636         }
12637         
12638         //this.trigger.addClassOnOver('x-form-trigger-over');
12639         //this.trigger.addClassOnClick('x-form-trigger-click');
12640         
12641         //if(!this.width){
12642         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12643         //}
12644     },
12645     
12646     closeTriggerEl : function()
12647     {
12648         var close = this.el.select('.roo-combo-removable-btn', true).first();
12649         return close ? close : false;
12650     },
12651     
12652     removeBtnClick : function(e, h, el)
12653     {
12654         e.preventDefault();
12655         
12656         if(this.fireEvent("remove", this) !== false){
12657             this.reset();
12658             this.fireEvent("afterremove", this)
12659         }
12660     },
12661     
12662     createList : function()
12663     {
12664         this.list = Roo.get(document.body).createChild({
12665             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12666             cls: 'typeahead typeahead-long dropdown-menu shadow',
12667             style: 'display:none'
12668         });
12669         
12670         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12671         
12672     },
12673
12674     // private
12675     initTrigger : function(){
12676        
12677     },
12678
12679     // private
12680     onDestroy : function(){
12681         if(this.trigger){
12682             this.trigger.removeAllListeners();
12683           //  this.trigger.remove();
12684         }
12685         //if(this.wrap){
12686         //    this.wrap.remove();
12687         //}
12688         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12689     },
12690
12691     // private
12692     onFocus : function(){
12693         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12694         /*
12695         if(!this.mimicing){
12696             this.wrap.addClass('x-trigger-wrap-focus');
12697             this.mimicing = true;
12698             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12699             if(this.monitorTab){
12700                 this.el.on("keydown", this.checkTab, this);
12701             }
12702         }
12703         */
12704     },
12705
12706     // private
12707     checkTab : function(e){
12708         if(e.getKey() == e.TAB){
12709             this.triggerBlur();
12710         }
12711     },
12712
12713     // private
12714     onBlur : function(){
12715         // do nothing
12716     },
12717
12718     // private
12719     mimicBlur : function(e, t){
12720         /*
12721         if(!this.wrap.contains(t) && this.validateBlur()){
12722             this.triggerBlur();
12723         }
12724         */
12725     },
12726
12727     // private
12728     triggerBlur : function(){
12729         this.mimicing = false;
12730         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12731         if(this.monitorTab){
12732             this.el.un("keydown", this.checkTab, this);
12733         }
12734         //this.wrap.removeClass('x-trigger-wrap-focus');
12735         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12736     },
12737
12738     // private
12739     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12740     validateBlur : function(e, t){
12741         return true;
12742     },
12743
12744     // private
12745     onDisable : function(){
12746         this.inputEl().dom.disabled = true;
12747         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12748         //if(this.wrap){
12749         //    this.wrap.addClass('x-item-disabled');
12750         //}
12751     },
12752
12753     // private
12754     onEnable : function(){
12755         this.inputEl().dom.disabled = false;
12756         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12757         //if(this.wrap){
12758         //    this.el.removeClass('x-item-disabled');
12759         //}
12760     },
12761
12762     // private
12763     onShow : function(){
12764         var ae = this.getActionEl();
12765         
12766         if(ae){
12767             ae.dom.style.display = '';
12768             ae.dom.style.visibility = 'visible';
12769         }
12770     },
12771
12772     // private
12773     
12774     onHide : function(){
12775         var ae = this.getActionEl();
12776         ae.dom.style.display = 'none';
12777     },
12778
12779     /**
12780      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12781      * by an implementing function.
12782      * @method
12783      * @param {EventObject} e
12784      */
12785     onTriggerClick : Roo.emptyFn
12786 });
12787  
12788 /*
12789 * Licence: LGPL
12790 */
12791
12792 /**
12793  * @class Roo.bootstrap.CardUploader
12794  * @extends Roo.bootstrap.Button
12795  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12796  * @cfg {Number} errorTimeout default 3000
12797  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12798  * @cfg {Array}  html The button text.
12799
12800  *
12801  * @constructor
12802  * Create a new CardUploader
12803  * @param {Object} config The config object
12804  */
12805
12806 Roo.bootstrap.CardUploader = function(config){
12807     
12808  
12809     
12810     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12811     
12812     
12813     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12814         return r.data.id
12815      });
12816     
12817      this.addEvents({
12818          // raw events
12819         /**
12820          * @event preview
12821          * When a image is clicked on - and needs to display a slideshow or similar..
12822          * @param {Roo.bootstrap.Card} this
12823          * @param {Object} The image information data 
12824          *
12825          */
12826         'preview' : true,
12827          /**
12828          * @event download
12829          * When a the download link is clicked
12830          * @param {Roo.bootstrap.Card} this
12831          * @param {Object} The image information data  contains 
12832          */
12833         'download' : true
12834         
12835     });
12836 };
12837  
12838 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12839     
12840      
12841     errorTimeout : 3000,
12842      
12843     images : false,
12844    
12845     fileCollection : false,
12846     allowBlank : true,
12847     
12848     getAutoCreate : function()
12849     {
12850         
12851         var cfg =  {
12852             cls :'form-group' ,
12853             cn : [
12854                
12855                 {
12856                     tag: 'label',
12857                    //cls : 'input-group-addon',
12858                     html : this.fieldLabel
12859
12860                 },
12861
12862                 {
12863                     tag: 'input',
12864                     type : 'hidden',
12865                     name : this.name,
12866                     value : this.value,
12867                     cls : 'd-none  form-control'
12868                 },
12869                 
12870                 {
12871                     tag: 'input',
12872                     multiple : 'multiple',
12873                     type : 'file',
12874                     cls : 'd-none  roo-card-upload-selector'
12875                 },
12876                 
12877                 {
12878                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12879                 },
12880                 {
12881                     cls : 'card-columns roo-card-uploader-container'
12882                 }
12883
12884             ]
12885         };
12886            
12887          
12888         return cfg;
12889     },
12890     
12891     getChildContainer : function() /// what children are added to.
12892     {
12893         return this.containerEl;
12894     },
12895    
12896     getButtonContainer : function() /// what children are added to.
12897     {
12898         return this.el.select(".roo-card-uploader-button-container").first();
12899     },
12900    
12901     initEvents : function()
12902     {
12903         
12904         Roo.bootstrap.Input.prototype.initEvents.call(this);
12905         
12906         var t = this;
12907         this.addxtype({
12908             xns: Roo.bootstrap,
12909
12910             xtype : 'Button',
12911             container_method : 'getButtonContainer' ,            
12912             html :  this.html, // fix changable?
12913             cls : 'w-100 ',
12914             listeners : {
12915                 'click' : function(btn, e) {
12916                     t.onClick(e);
12917                 }
12918             }
12919         });
12920         
12921         
12922         
12923         
12924         this.urlAPI = (window.createObjectURL && window) || 
12925                                 (window.URL && URL.revokeObjectURL && URL) || 
12926                                 (window.webkitURL && webkitURL);
12927                         
12928          
12929          
12930          
12931         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12932         
12933         this.selectorEl.on('change', this.onFileSelected, this);
12934         if (this.images) {
12935             var t = this;
12936             this.images.forEach(function(img) {
12937                 t.addCard(img)
12938             });
12939             this.images = false;
12940         }
12941         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12942          
12943        
12944     },
12945     
12946    
12947     onClick : function(e)
12948     {
12949         e.preventDefault();
12950          
12951         this.selectorEl.dom.click();
12952          
12953     },
12954     
12955     onFileSelected : function(e)
12956     {
12957         e.preventDefault();
12958         
12959         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12960             return;
12961         }
12962         
12963         Roo.each(this.selectorEl.dom.files, function(file){    
12964             this.addFile(file);
12965         }, this);
12966          
12967     },
12968     
12969       
12970     
12971       
12972     
12973     addFile : function(file)
12974     {
12975            
12976         if(typeof(file) === 'string'){
12977             throw "Add file by name?"; // should not happen
12978             return;
12979         }
12980         
12981         if(!file || !this.urlAPI){
12982             return;
12983         }
12984         
12985         // file;
12986         // file.type;
12987         
12988         var _this = this;
12989         
12990         
12991         var url = _this.urlAPI.createObjectURL( file);
12992            
12993         this.addCard({
12994             id : Roo.bootstrap.CardUploader.ID--,
12995             is_uploaded : false,
12996             src : url,
12997             srcfile : file,
12998             title : file.name,
12999             mimetype : file.type,
13000             preview : false,
13001             is_deleted : 0
13002         });
13003         
13004     },
13005     
13006     /**
13007      * addCard - add an Attachment to the uploader
13008      * @param data - the data about the image to upload
13009      *
13010      * {
13011           id : 123
13012           title : "Title of file",
13013           is_uploaded : false,
13014           src : "http://.....",
13015           srcfile : { the File upload object },
13016           mimetype : file.type,
13017           preview : false,
13018           is_deleted : 0
13019           .. any other data...
13020         }
13021      *
13022      * 
13023     */
13024     
13025     addCard : function (data)
13026     {
13027         // hidden input element?
13028         // if the file is not an image...
13029         //then we need to use something other that and header_image
13030         var t = this;
13031         //   remove.....
13032         var footer = [
13033             {
13034                 xns : Roo.bootstrap,
13035                 xtype : 'CardFooter',
13036                  items: [
13037                     {
13038                         xns : Roo.bootstrap,
13039                         xtype : 'Element',
13040                         cls : 'd-flex',
13041                         items : [
13042                             
13043                             {
13044                                 xns : Roo.bootstrap,
13045                                 xtype : 'Button',
13046                                 html : String.format("<small>{0}</small>", data.title),
13047                                 cls : 'col-10 text-left',
13048                                 size: 'sm',
13049                                 weight: 'link',
13050                                 fa : 'download',
13051                                 listeners : {
13052                                     click : function() {
13053                                      
13054                                         t.fireEvent( "download", t, data );
13055                                     }
13056                                 }
13057                             },
13058                           
13059                             {
13060                                 xns : Roo.bootstrap,
13061                                 xtype : 'Button',
13062                                 style: 'max-height: 28px; ',
13063                                 size : 'sm',
13064                                 weight: 'danger',
13065                                 cls : 'col-2',
13066                                 fa : 'times',
13067                                 listeners : {
13068                                     click : function() {
13069                                         t.removeCard(data.id)
13070                                     }
13071                                 }
13072                             }
13073                         ]
13074                     }
13075                     
13076                 ] 
13077             }
13078             
13079         ];
13080         
13081         var cn = this.addxtype(
13082             {
13083                  
13084                 xns : Roo.bootstrap,
13085                 xtype : 'Card',
13086                 closeable : true,
13087                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13088                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13089                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13090                 data : data,
13091                 html : false,
13092                  
13093                 items : footer,
13094                 initEvents : function() {
13095                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13096                     var card = this;
13097                     this.imgEl = this.el.select('.card-img-top').first();
13098                     if (this.imgEl) {
13099                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13100                         this.imgEl.set({ 'pointer' : 'cursor' });
13101                                   
13102                     }
13103                     this.getCardFooter().addClass('p-1');
13104                     
13105                   
13106                 }
13107                 
13108             }
13109         );
13110         // dont' really need ot update items.
13111         // this.items.push(cn);
13112         this.fileCollection.add(cn);
13113         
13114         if (!data.srcfile) {
13115             this.updateInput();
13116             return;
13117         }
13118             
13119         var _t = this;
13120         var reader = new FileReader();
13121         reader.addEventListener("load", function() {  
13122             data.srcdata =  reader.result;
13123             _t.updateInput();
13124         });
13125         reader.readAsDataURL(data.srcfile);
13126         
13127         
13128         
13129     },
13130     removeCard : function(id)
13131     {
13132         
13133         var card  = this.fileCollection.get(id);
13134         card.data.is_deleted = 1;
13135         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13136         //this.fileCollection.remove(card);
13137         //this.items = this.items.filter(function(e) { return e != card });
13138         // dont' really need ot update items.
13139         card.el.dom.parentNode.removeChild(card.el.dom);
13140         this.updateInput();
13141
13142         
13143     },
13144     reset: function()
13145     {
13146         this.fileCollection.each(function(card) {
13147             if (card.el.dom && card.el.dom.parentNode) {
13148                 card.el.dom.parentNode.removeChild(card.el.dom);
13149             }
13150         });
13151         this.fileCollection.clear();
13152         this.updateInput();
13153     },
13154     
13155     updateInput : function()
13156     {
13157          var data = [];
13158         this.fileCollection.each(function(e) {
13159             data.push(e.data);
13160             
13161         });
13162         this.inputEl().dom.value = JSON.stringify(data);
13163         
13164         
13165         
13166     }
13167     
13168     
13169 });
13170
13171
13172 Roo.bootstrap.CardUploader.ID = -1;/*
13173  * Based on:
13174  * Ext JS Library 1.1.1
13175  * Copyright(c) 2006-2007, Ext JS, LLC.
13176  *
13177  * Originally Released Under LGPL - original licence link has changed is not relivant.
13178  *
13179  * Fork - LGPL
13180  * <script type="text/javascript">
13181  */
13182
13183
13184 /**
13185  * @class Roo.data.SortTypes
13186  * @singleton
13187  * Defines the default sorting (casting?) comparison functions used when sorting data.
13188  */
13189 Roo.data.SortTypes = {
13190     /**
13191      * Default sort that does nothing
13192      * @param {Mixed} s The value being converted
13193      * @return {Mixed} The comparison value
13194      */
13195     none : function(s){
13196         return s;
13197     },
13198     
13199     /**
13200      * The regular expression used to strip tags
13201      * @type {RegExp}
13202      * @property
13203      */
13204     stripTagsRE : /<\/?[^>]+>/gi,
13205     
13206     /**
13207      * Strips all HTML tags to sort on text only
13208      * @param {Mixed} s The value being converted
13209      * @return {String} The comparison value
13210      */
13211     asText : function(s){
13212         return String(s).replace(this.stripTagsRE, "");
13213     },
13214     
13215     /**
13216      * Strips all HTML tags to sort on text only - Case insensitive
13217      * @param {Mixed} s The value being converted
13218      * @return {String} The comparison value
13219      */
13220     asUCText : function(s){
13221         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13222     },
13223     
13224     /**
13225      * Case insensitive string
13226      * @param {Mixed} s The value being converted
13227      * @return {String} The comparison value
13228      */
13229     asUCString : function(s) {
13230         return String(s).toUpperCase();
13231     },
13232     
13233     /**
13234      * Date sorting
13235      * @param {Mixed} s The value being converted
13236      * @return {Number} The comparison value
13237      */
13238     asDate : function(s) {
13239         if(!s){
13240             return 0;
13241         }
13242         if(s instanceof Date){
13243             return s.getTime();
13244         }
13245         return Date.parse(String(s));
13246     },
13247     
13248     /**
13249      * Float sorting
13250      * @param {Mixed} s The value being converted
13251      * @return {Float} The comparison value
13252      */
13253     asFloat : function(s) {
13254         var val = parseFloat(String(s).replace(/,/g, ""));
13255         if(isNaN(val)) {
13256             val = 0;
13257         }
13258         return val;
13259     },
13260     
13261     /**
13262      * Integer sorting
13263      * @param {Mixed} s The value being converted
13264      * @return {Number} The comparison value
13265      */
13266     asInt : function(s) {
13267         var val = parseInt(String(s).replace(/,/g, ""));
13268         if(isNaN(val)) {
13269             val = 0;
13270         }
13271         return val;
13272     }
13273 };/*
13274  * Based on:
13275  * Ext JS Library 1.1.1
13276  * Copyright(c) 2006-2007, Ext JS, LLC.
13277  *
13278  * Originally Released Under LGPL - original licence link has changed is not relivant.
13279  *
13280  * Fork - LGPL
13281  * <script type="text/javascript">
13282  */
13283
13284 /**
13285 * @class Roo.data.Record
13286  * Instances of this class encapsulate both record <em>definition</em> information, and record
13287  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13288  * to access Records cached in an {@link Roo.data.Store} object.<br>
13289  * <p>
13290  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13291  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13292  * objects.<br>
13293  * <p>
13294  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13295  * @constructor
13296  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13297  * {@link #create}. The parameters are the same.
13298  * @param {Array} data An associative Array of data values keyed by the field name.
13299  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13300  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13301  * not specified an integer id is generated.
13302  */
13303 Roo.data.Record = function(data, id){
13304     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13305     this.data = data;
13306 };
13307
13308 /**
13309  * Generate a constructor for a specific record layout.
13310  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13311  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13312  * Each field definition object may contain the following properties: <ul>
13313  * <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,
13314  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13315  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13316  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13317  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13318  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13319  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13320  * this may be omitted.</p></li>
13321  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13322  * <ul><li>auto (Default, implies no conversion)</li>
13323  * <li>string</li>
13324  * <li>int</li>
13325  * <li>float</li>
13326  * <li>boolean</li>
13327  * <li>date</li></ul></p></li>
13328  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13329  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13330  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13331  * by the Reader into an object that will be stored in the Record. It is passed the
13332  * following parameters:<ul>
13333  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13334  * </ul></p></li>
13335  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13336  * </ul>
13337  * <br>usage:<br><pre><code>
13338 var TopicRecord = Roo.data.Record.create(
13339     {name: 'title', mapping: 'topic_title'},
13340     {name: 'author', mapping: 'username'},
13341     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13342     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13343     {name: 'lastPoster', mapping: 'user2'},
13344     {name: 'excerpt', mapping: 'post_text'}
13345 );
13346
13347 var myNewRecord = new TopicRecord({
13348     title: 'Do my job please',
13349     author: 'noobie',
13350     totalPosts: 1,
13351     lastPost: new Date(),
13352     lastPoster: 'Animal',
13353     excerpt: 'No way dude!'
13354 });
13355 myStore.add(myNewRecord);
13356 </code></pre>
13357  * @method create
13358  * @static
13359  */
13360 Roo.data.Record.create = function(o){
13361     var f = function(){
13362         f.superclass.constructor.apply(this, arguments);
13363     };
13364     Roo.extend(f, Roo.data.Record);
13365     var p = f.prototype;
13366     p.fields = new Roo.util.MixedCollection(false, function(field){
13367         return field.name;
13368     });
13369     for(var i = 0, len = o.length; i < len; i++){
13370         p.fields.add(new Roo.data.Field(o[i]));
13371     }
13372     f.getField = function(name){
13373         return p.fields.get(name);  
13374     };
13375     return f;
13376 };
13377
13378 Roo.data.Record.AUTO_ID = 1000;
13379 Roo.data.Record.EDIT = 'edit';
13380 Roo.data.Record.REJECT = 'reject';
13381 Roo.data.Record.COMMIT = 'commit';
13382
13383 Roo.data.Record.prototype = {
13384     /**
13385      * Readonly flag - true if this record has been modified.
13386      * @type Boolean
13387      */
13388     dirty : false,
13389     editing : false,
13390     error: null,
13391     modified: null,
13392
13393     // private
13394     join : function(store){
13395         this.store = store;
13396     },
13397
13398     /**
13399      * Set the named field to the specified value.
13400      * @param {String} name The name of the field to set.
13401      * @param {Object} value The value to set the field to.
13402      */
13403     set : function(name, value){
13404         if(this.data[name] == value){
13405             return;
13406         }
13407         this.dirty = true;
13408         if(!this.modified){
13409             this.modified = {};
13410         }
13411         if(typeof this.modified[name] == 'undefined'){
13412             this.modified[name] = this.data[name];
13413         }
13414         this.data[name] = value;
13415         if(!this.editing && this.store){
13416             this.store.afterEdit(this);
13417         }       
13418     },
13419
13420     /**
13421      * Get the value of the named field.
13422      * @param {String} name The name of the field to get the value of.
13423      * @return {Object} The value of the field.
13424      */
13425     get : function(name){
13426         return this.data[name]; 
13427     },
13428
13429     // private
13430     beginEdit : function(){
13431         this.editing = true;
13432         this.modified = {}; 
13433     },
13434
13435     // private
13436     cancelEdit : function(){
13437         this.editing = false;
13438         delete this.modified;
13439     },
13440
13441     // private
13442     endEdit : function(){
13443         this.editing = false;
13444         if(this.dirty && this.store){
13445             this.store.afterEdit(this);
13446         }
13447     },
13448
13449     /**
13450      * Usually called by the {@link Roo.data.Store} which owns the Record.
13451      * Rejects all changes made to the Record since either creation, or the last commit operation.
13452      * Modified fields are reverted to their original values.
13453      * <p>
13454      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13455      * of reject operations.
13456      */
13457     reject : function(){
13458         var m = this.modified;
13459         for(var n in m){
13460             if(typeof m[n] != "function"){
13461                 this.data[n] = m[n];
13462             }
13463         }
13464         this.dirty = false;
13465         delete this.modified;
13466         this.editing = false;
13467         if(this.store){
13468             this.store.afterReject(this);
13469         }
13470     },
13471
13472     /**
13473      * Usually called by the {@link Roo.data.Store} which owns the Record.
13474      * Commits all changes made to the Record since either creation, or the last commit operation.
13475      * <p>
13476      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13477      * of commit operations.
13478      */
13479     commit : function(){
13480         this.dirty = false;
13481         delete this.modified;
13482         this.editing = false;
13483         if(this.store){
13484             this.store.afterCommit(this);
13485         }
13486     },
13487
13488     // private
13489     hasError : function(){
13490         return this.error != null;
13491     },
13492
13493     // private
13494     clearError : function(){
13495         this.error = null;
13496     },
13497
13498     /**
13499      * Creates a copy of this record.
13500      * @param {String} id (optional) A new record id if you don't want to use this record's id
13501      * @return {Record}
13502      */
13503     copy : function(newId) {
13504         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13505     }
13506 };/*
13507  * Based on:
13508  * Ext JS Library 1.1.1
13509  * Copyright(c) 2006-2007, Ext JS, LLC.
13510  *
13511  * Originally Released Under LGPL - original licence link has changed is not relivant.
13512  *
13513  * Fork - LGPL
13514  * <script type="text/javascript">
13515  */
13516
13517
13518
13519 /**
13520  * @class Roo.data.Store
13521  * @extends Roo.util.Observable
13522  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13523  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13524  * <p>
13525  * 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
13526  * has no knowledge of the format of the data returned by the Proxy.<br>
13527  * <p>
13528  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13529  * instances from the data object. These records are cached and made available through accessor functions.
13530  * @constructor
13531  * Creates a new Store.
13532  * @param {Object} config A config object containing the objects needed for the Store to access data,
13533  * and read the data into Records.
13534  */
13535 Roo.data.Store = function(config){
13536     this.data = new Roo.util.MixedCollection(false);
13537     this.data.getKey = function(o){
13538         return o.id;
13539     };
13540     this.baseParams = {};
13541     // private
13542     this.paramNames = {
13543         "start" : "start",
13544         "limit" : "limit",
13545         "sort" : "sort",
13546         "dir" : "dir",
13547         "multisort" : "_multisort"
13548     };
13549
13550     if(config && config.data){
13551         this.inlineData = config.data;
13552         delete config.data;
13553     }
13554
13555     Roo.apply(this, config);
13556     
13557     if(this.reader){ // reader passed
13558         this.reader = Roo.factory(this.reader, Roo.data);
13559         this.reader.xmodule = this.xmodule || false;
13560         if(!this.recordType){
13561             this.recordType = this.reader.recordType;
13562         }
13563         if(this.reader.onMetaChange){
13564             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13565         }
13566     }
13567
13568     if(this.recordType){
13569         this.fields = this.recordType.prototype.fields;
13570     }
13571     this.modified = [];
13572
13573     this.addEvents({
13574         /**
13575          * @event datachanged
13576          * Fires when the data cache has changed, and a widget which is using this Store
13577          * as a Record cache should refresh its view.
13578          * @param {Store} this
13579          */
13580         datachanged : true,
13581         /**
13582          * @event metachange
13583          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13584          * @param {Store} this
13585          * @param {Object} meta The JSON metadata
13586          */
13587         metachange : true,
13588         /**
13589          * @event add
13590          * Fires when Records have been added to the Store
13591          * @param {Store} this
13592          * @param {Roo.data.Record[]} records The array of Records added
13593          * @param {Number} index The index at which the record(s) were added
13594          */
13595         add : true,
13596         /**
13597          * @event remove
13598          * Fires when a Record has been removed from the Store
13599          * @param {Store} this
13600          * @param {Roo.data.Record} record The Record that was removed
13601          * @param {Number} index The index at which the record was removed
13602          */
13603         remove : true,
13604         /**
13605          * @event update
13606          * Fires when a Record has been updated
13607          * @param {Store} this
13608          * @param {Roo.data.Record} record The Record that was updated
13609          * @param {String} operation The update operation being performed.  Value may be one of:
13610          * <pre><code>
13611  Roo.data.Record.EDIT
13612  Roo.data.Record.REJECT
13613  Roo.data.Record.COMMIT
13614          * </code></pre>
13615          */
13616         update : true,
13617         /**
13618          * @event clear
13619          * Fires when the data cache has been cleared.
13620          * @param {Store} this
13621          */
13622         clear : true,
13623         /**
13624          * @event beforeload
13625          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13626          * the load action will be canceled.
13627          * @param {Store} this
13628          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13629          */
13630         beforeload : true,
13631         /**
13632          * @event beforeloadadd
13633          * Fires after a new set of Records has been loaded.
13634          * @param {Store} this
13635          * @param {Roo.data.Record[]} records The Records that were loaded
13636          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13637          */
13638         beforeloadadd : true,
13639         /**
13640          * @event load
13641          * Fires after a new set of Records has been loaded, before they are added to the store.
13642          * @param {Store} this
13643          * @param {Roo.data.Record[]} records The Records that were loaded
13644          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13645          * @params {Object} return from reader
13646          */
13647         load : true,
13648         /**
13649          * @event loadexception
13650          * Fires if an exception occurs in the Proxy during loading.
13651          * Called with the signature of the Proxy's "loadexception" event.
13652          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13653          * 
13654          * @param {Proxy} 
13655          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13656          * @param {Object} load options 
13657          * @param {Object} jsonData from your request (normally this contains the Exception)
13658          */
13659         loadexception : true
13660     });
13661     
13662     if(this.proxy){
13663         this.proxy = Roo.factory(this.proxy, Roo.data);
13664         this.proxy.xmodule = this.xmodule || false;
13665         this.relayEvents(this.proxy,  ["loadexception"]);
13666     }
13667     this.sortToggle = {};
13668     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13669
13670     Roo.data.Store.superclass.constructor.call(this);
13671
13672     if(this.inlineData){
13673         this.loadData(this.inlineData);
13674         delete this.inlineData;
13675     }
13676 };
13677
13678 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13679      /**
13680     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13681     * without a remote query - used by combo/forms at present.
13682     */
13683     
13684     /**
13685     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13686     */
13687     /**
13688     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13689     */
13690     /**
13691     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13692     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13693     */
13694     /**
13695     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13696     * on any HTTP request
13697     */
13698     /**
13699     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13700     */
13701     /**
13702     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13703     */
13704     multiSort: false,
13705     /**
13706     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13707     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13708     */
13709     remoteSort : false,
13710
13711     /**
13712     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13713      * loaded or when a record is removed. (defaults to false).
13714     */
13715     pruneModifiedRecords : false,
13716
13717     // private
13718     lastOptions : null,
13719
13720     /**
13721      * Add Records to the Store and fires the add event.
13722      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13723      */
13724     add : function(records){
13725         records = [].concat(records);
13726         for(var i = 0, len = records.length; i < len; i++){
13727             records[i].join(this);
13728         }
13729         var index = this.data.length;
13730         this.data.addAll(records);
13731         this.fireEvent("add", this, records, index);
13732     },
13733
13734     /**
13735      * Remove a Record from the Store and fires the remove event.
13736      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13737      */
13738     remove : function(record){
13739         var index = this.data.indexOf(record);
13740         this.data.removeAt(index);
13741  
13742         if(this.pruneModifiedRecords){
13743             this.modified.remove(record);
13744         }
13745         this.fireEvent("remove", this, record, index);
13746     },
13747
13748     /**
13749      * Remove all Records from the Store and fires the clear event.
13750      */
13751     removeAll : function(){
13752         this.data.clear();
13753         if(this.pruneModifiedRecords){
13754             this.modified = [];
13755         }
13756         this.fireEvent("clear", this);
13757     },
13758
13759     /**
13760      * Inserts Records to the Store at the given index and fires the add event.
13761      * @param {Number} index The start index at which to insert the passed Records.
13762      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13763      */
13764     insert : function(index, records){
13765         records = [].concat(records);
13766         for(var i = 0, len = records.length; i < len; i++){
13767             this.data.insert(index, records[i]);
13768             records[i].join(this);
13769         }
13770         this.fireEvent("add", this, records, index);
13771     },
13772
13773     /**
13774      * Get the index within the cache of the passed Record.
13775      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13776      * @return {Number} The index of the passed Record. Returns -1 if not found.
13777      */
13778     indexOf : function(record){
13779         return this.data.indexOf(record);
13780     },
13781
13782     /**
13783      * Get the index within the cache of the Record with the passed id.
13784      * @param {String} id The id of the Record to find.
13785      * @return {Number} The index of the Record. Returns -1 if not found.
13786      */
13787     indexOfId : function(id){
13788         return this.data.indexOfKey(id);
13789     },
13790
13791     /**
13792      * Get the Record with the specified id.
13793      * @param {String} id The id of the Record to find.
13794      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13795      */
13796     getById : function(id){
13797         return this.data.key(id);
13798     },
13799
13800     /**
13801      * Get the Record at the specified index.
13802      * @param {Number} index The index of the Record to find.
13803      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13804      */
13805     getAt : function(index){
13806         return this.data.itemAt(index);
13807     },
13808
13809     /**
13810      * Returns a range of Records between specified indices.
13811      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13812      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13813      * @return {Roo.data.Record[]} An array of Records
13814      */
13815     getRange : function(start, end){
13816         return this.data.getRange(start, end);
13817     },
13818
13819     // private
13820     storeOptions : function(o){
13821         o = Roo.apply({}, o);
13822         delete o.callback;
13823         delete o.scope;
13824         this.lastOptions = o;
13825     },
13826
13827     /**
13828      * Loads the Record cache from the configured Proxy using the configured Reader.
13829      * <p>
13830      * If using remote paging, then the first load call must specify the <em>start</em>
13831      * and <em>limit</em> properties in the options.params property to establish the initial
13832      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13833      * <p>
13834      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13835      * and this call will return before the new data has been loaded. Perform any post-processing
13836      * in a callback function, or in a "load" event handler.</strong>
13837      * <p>
13838      * @param {Object} options An object containing properties which control loading options:<ul>
13839      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13840      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13841      * passed the following arguments:<ul>
13842      * <li>r : Roo.data.Record[]</li>
13843      * <li>options: Options object from the load call</li>
13844      * <li>success: Boolean success indicator</li></ul></li>
13845      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13846      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13847      * </ul>
13848      */
13849     load : function(options){
13850         options = options || {};
13851         if(this.fireEvent("beforeload", this, options) !== false){
13852             this.storeOptions(options);
13853             var p = Roo.apply(options.params || {}, this.baseParams);
13854             // if meta was not loaded from remote source.. try requesting it.
13855             if (!this.reader.metaFromRemote) {
13856                 p._requestMeta = 1;
13857             }
13858             if(this.sortInfo && this.remoteSort){
13859                 var pn = this.paramNames;
13860                 p[pn["sort"]] = this.sortInfo.field;
13861                 p[pn["dir"]] = this.sortInfo.direction;
13862             }
13863             if (this.multiSort) {
13864                 var pn = this.paramNames;
13865                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13866             }
13867             
13868             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13869         }
13870     },
13871
13872     /**
13873      * Reloads the Record cache from the configured Proxy using the configured Reader and
13874      * the options from the last load operation performed.
13875      * @param {Object} options (optional) An object containing properties which may override the options
13876      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13877      * the most recently used options are reused).
13878      */
13879     reload : function(options){
13880         this.load(Roo.applyIf(options||{}, this.lastOptions));
13881     },
13882
13883     // private
13884     // Called as a callback by the Reader during a load operation.
13885     loadRecords : function(o, options, success){
13886         if(!o || success === false){
13887             if(success !== false){
13888                 this.fireEvent("load", this, [], options, o);
13889             }
13890             if(options.callback){
13891                 options.callback.call(options.scope || this, [], options, false);
13892             }
13893             return;
13894         }
13895         // if data returned failure - throw an exception.
13896         if (o.success === false) {
13897             // show a message if no listener is registered.
13898             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13899                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13900             }
13901             // loadmask wil be hooked into this..
13902             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13903             return;
13904         }
13905         var r = o.records, t = o.totalRecords || r.length;
13906         
13907         this.fireEvent("beforeloadadd", this, r, options, o);
13908         
13909         if(!options || options.add !== true){
13910             if(this.pruneModifiedRecords){
13911                 this.modified = [];
13912             }
13913             for(var i = 0, len = r.length; i < len; i++){
13914                 r[i].join(this);
13915             }
13916             if(this.snapshot){
13917                 this.data = this.snapshot;
13918                 delete this.snapshot;
13919             }
13920             this.data.clear();
13921             this.data.addAll(r);
13922             this.totalLength = t;
13923             this.applySort();
13924             this.fireEvent("datachanged", this);
13925         }else{
13926             this.totalLength = Math.max(t, this.data.length+r.length);
13927             this.add(r);
13928         }
13929         
13930         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13931                 
13932             var e = new Roo.data.Record({});
13933
13934             e.set(this.parent.displayField, this.parent.emptyTitle);
13935             e.set(this.parent.valueField, '');
13936
13937             this.insert(0, e);
13938         }
13939             
13940         this.fireEvent("load", this, r, options, o);
13941         if(options.callback){
13942             options.callback.call(options.scope || this, r, options, true);
13943         }
13944     },
13945
13946
13947     /**
13948      * Loads data from a passed data block. A Reader which understands the format of the data
13949      * must have been configured in the constructor.
13950      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13951      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13952      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13953      */
13954     loadData : function(o, append){
13955         var r = this.reader.readRecords(o);
13956         this.loadRecords(r, {add: append}, true);
13957     },
13958     
13959      /**
13960      * using 'cn' the nested child reader read the child array into it's child stores.
13961      * @param {Object} rec The record with a 'children array
13962      */
13963     loadDataFromChildren : function(rec)
13964     {
13965         this.loadData(this.reader.toLoadData(rec));
13966     },
13967     
13968
13969     /**
13970      * Gets the number of cached records.
13971      * <p>
13972      * <em>If using paging, this may not be the total size of the dataset. If the data object
13973      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13974      * the data set size</em>
13975      */
13976     getCount : function(){
13977         return this.data.length || 0;
13978     },
13979
13980     /**
13981      * Gets the total number of records in the dataset as returned by the server.
13982      * <p>
13983      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13984      * the dataset size</em>
13985      */
13986     getTotalCount : function(){
13987         return this.totalLength || 0;
13988     },
13989
13990     /**
13991      * Returns the sort state of the Store as an object with two properties:
13992      * <pre><code>
13993  field {String} The name of the field by which the Records are sorted
13994  direction {String} The sort order, "ASC" or "DESC"
13995      * </code></pre>
13996      */
13997     getSortState : function(){
13998         return this.sortInfo;
13999     },
14000
14001     // private
14002     applySort : function(){
14003         if(this.sortInfo && !this.remoteSort){
14004             var s = this.sortInfo, f = s.field;
14005             var st = this.fields.get(f).sortType;
14006             var fn = function(r1, r2){
14007                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14008                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14009             };
14010             this.data.sort(s.direction, fn);
14011             if(this.snapshot && this.snapshot != this.data){
14012                 this.snapshot.sort(s.direction, fn);
14013             }
14014         }
14015     },
14016
14017     /**
14018      * Sets the default sort column and order to be used by the next load operation.
14019      * @param {String} fieldName The name of the field to sort by.
14020      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14021      */
14022     setDefaultSort : function(field, dir){
14023         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14024     },
14025
14026     /**
14027      * Sort the Records.
14028      * If remote sorting is used, the sort is performed on the server, and the cache is
14029      * reloaded. If local sorting is used, the cache is sorted internally.
14030      * @param {String} fieldName The name of the field to sort by.
14031      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14032      */
14033     sort : function(fieldName, dir){
14034         var f = this.fields.get(fieldName);
14035         if(!dir){
14036             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14037             
14038             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14039                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14040             }else{
14041                 dir = f.sortDir;
14042             }
14043         }
14044         this.sortToggle[f.name] = dir;
14045         this.sortInfo = {field: f.name, direction: dir};
14046         if(!this.remoteSort){
14047             this.applySort();
14048             this.fireEvent("datachanged", this);
14049         }else{
14050             this.load(this.lastOptions);
14051         }
14052     },
14053
14054     /**
14055      * Calls the specified function for each of the Records in the cache.
14056      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14057      * Returning <em>false</em> aborts and exits the iteration.
14058      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14059      */
14060     each : function(fn, scope){
14061         this.data.each(fn, scope);
14062     },
14063
14064     /**
14065      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14066      * (e.g., during paging).
14067      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14068      */
14069     getModifiedRecords : function(){
14070         return this.modified;
14071     },
14072
14073     // private
14074     createFilterFn : function(property, value, anyMatch){
14075         if(!value.exec){ // not a regex
14076             value = String(value);
14077             if(value.length == 0){
14078                 return false;
14079             }
14080             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14081         }
14082         return function(r){
14083             return value.test(r.data[property]);
14084         };
14085     },
14086
14087     /**
14088      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14089      * @param {String} property A field on your records
14090      * @param {Number} start The record index to start at (defaults to 0)
14091      * @param {Number} end The last record index to include (defaults to length - 1)
14092      * @return {Number} The sum
14093      */
14094     sum : function(property, start, end){
14095         var rs = this.data.items, v = 0;
14096         start = start || 0;
14097         end = (end || end === 0) ? end : rs.length-1;
14098
14099         for(var i = start; i <= end; i++){
14100             v += (rs[i].data[property] || 0);
14101         }
14102         return v;
14103     },
14104
14105     /**
14106      * Filter the records by a specified property.
14107      * @param {String} field A field on your records
14108      * @param {String/RegExp} value Either a string that the field
14109      * should start with or a RegExp to test against the field
14110      * @param {Boolean} anyMatch True to match any part not just the beginning
14111      */
14112     filter : function(property, value, anyMatch){
14113         var fn = this.createFilterFn(property, value, anyMatch);
14114         return fn ? this.filterBy(fn) : this.clearFilter();
14115     },
14116
14117     /**
14118      * Filter by a function. The specified function will be called with each
14119      * record in this data source. If the function returns true the record is included,
14120      * otherwise it is filtered.
14121      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14122      * @param {Object} scope (optional) The scope of the function (defaults to this)
14123      */
14124     filterBy : function(fn, scope){
14125         this.snapshot = this.snapshot || this.data;
14126         this.data = this.queryBy(fn, scope||this);
14127         this.fireEvent("datachanged", this);
14128     },
14129
14130     /**
14131      * Query the records by a specified property.
14132      * @param {String} field A field on your records
14133      * @param {String/RegExp} value Either a string that the field
14134      * should start with or a RegExp to test against the field
14135      * @param {Boolean} anyMatch True to match any part not just the beginning
14136      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14137      */
14138     query : function(property, value, anyMatch){
14139         var fn = this.createFilterFn(property, value, anyMatch);
14140         return fn ? this.queryBy(fn) : this.data.clone();
14141     },
14142
14143     /**
14144      * Query by a function. The specified function will be called with each
14145      * record in this data source. If the function returns true the record is included
14146      * in the results.
14147      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14148      * @param {Object} scope (optional) The scope of the function (defaults to this)
14149       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14150      **/
14151     queryBy : function(fn, scope){
14152         var data = this.snapshot || this.data;
14153         return data.filterBy(fn, scope||this);
14154     },
14155
14156     /**
14157      * Collects unique values for a particular dataIndex from this store.
14158      * @param {String} dataIndex The property to collect
14159      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14160      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14161      * @return {Array} An array of the unique values
14162      **/
14163     collect : function(dataIndex, allowNull, bypassFilter){
14164         var d = (bypassFilter === true && this.snapshot) ?
14165                 this.snapshot.items : this.data.items;
14166         var v, sv, r = [], l = {};
14167         for(var i = 0, len = d.length; i < len; i++){
14168             v = d[i].data[dataIndex];
14169             sv = String(v);
14170             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14171                 l[sv] = true;
14172                 r[r.length] = v;
14173             }
14174         }
14175         return r;
14176     },
14177
14178     /**
14179      * Revert to a view of the Record cache with no filtering applied.
14180      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14181      */
14182     clearFilter : function(suppressEvent){
14183         if(this.snapshot && this.snapshot != this.data){
14184             this.data = this.snapshot;
14185             delete this.snapshot;
14186             if(suppressEvent !== true){
14187                 this.fireEvent("datachanged", this);
14188             }
14189         }
14190     },
14191
14192     // private
14193     afterEdit : function(record){
14194         if(this.modified.indexOf(record) == -1){
14195             this.modified.push(record);
14196         }
14197         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14198     },
14199     
14200     // private
14201     afterReject : function(record){
14202         this.modified.remove(record);
14203         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14204     },
14205
14206     // private
14207     afterCommit : function(record){
14208         this.modified.remove(record);
14209         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14210     },
14211
14212     /**
14213      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14214      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14215      */
14216     commitChanges : function(){
14217         var m = this.modified.slice(0);
14218         this.modified = [];
14219         for(var i = 0, len = m.length; i < len; i++){
14220             m[i].commit();
14221         }
14222     },
14223
14224     /**
14225      * Cancel outstanding changes on all changed records.
14226      */
14227     rejectChanges : function(){
14228         var m = this.modified.slice(0);
14229         this.modified = [];
14230         for(var i = 0, len = m.length; i < len; i++){
14231             m[i].reject();
14232         }
14233     },
14234
14235     onMetaChange : function(meta, rtype, o){
14236         this.recordType = rtype;
14237         this.fields = rtype.prototype.fields;
14238         delete this.snapshot;
14239         this.sortInfo = meta.sortInfo || this.sortInfo;
14240         this.modified = [];
14241         this.fireEvent('metachange', this, this.reader.meta);
14242     },
14243     
14244     moveIndex : function(data, type)
14245     {
14246         var index = this.indexOf(data);
14247         
14248         var newIndex = index + type;
14249         
14250         this.remove(data);
14251         
14252         this.insert(newIndex, data);
14253         
14254     }
14255 });/*
14256  * Based on:
14257  * Ext JS Library 1.1.1
14258  * Copyright(c) 2006-2007, Ext JS, LLC.
14259  *
14260  * Originally Released Under LGPL - original licence link has changed is not relivant.
14261  *
14262  * Fork - LGPL
14263  * <script type="text/javascript">
14264  */
14265
14266 /**
14267  * @class Roo.data.SimpleStore
14268  * @extends Roo.data.Store
14269  * Small helper class to make creating Stores from Array data easier.
14270  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14271  * @cfg {Array} fields An array of field definition objects, or field name strings.
14272  * @cfg {Object} an existing reader (eg. copied from another store)
14273  * @cfg {Array} data The multi-dimensional array of data
14274  * @constructor
14275  * @param {Object} config
14276  */
14277 Roo.data.SimpleStore = function(config)
14278 {
14279     Roo.data.SimpleStore.superclass.constructor.call(this, {
14280         isLocal : true,
14281         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14282                 id: config.id
14283             },
14284             Roo.data.Record.create(config.fields)
14285         ),
14286         proxy : new Roo.data.MemoryProxy(config.data)
14287     });
14288     this.load();
14289 };
14290 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14291  * Based on:
14292  * Ext JS Library 1.1.1
14293  * Copyright(c) 2006-2007, Ext JS, LLC.
14294  *
14295  * Originally Released Under LGPL - original licence link has changed is not relivant.
14296  *
14297  * Fork - LGPL
14298  * <script type="text/javascript">
14299  */
14300
14301 /**
14302 /**
14303  * @extends Roo.data.Store
14304  * @class Roo.data.JsonStore
14305  * Small helper class to make creating Stores for JSON data easier. <br/>
14306 <pre><code>
14307 var store = new Roo.data.JsonStore({
14308     url: 'get-images.php',
14309     root: 'images',
14310     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14311 });
14312 </code></pre>
14313  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14314  * JsonReader and HttpProxy (unless inline data is provided).</b>
14315  * @cfg {Array} fields An array of field definition objects, or field name strings.
14316  * @constructor
14317  * @param {Object} config
14318  */
14319 Roo.data.JsonStore = function(c){
14320     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14321         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14322         reader: new Roo.data.JsonReader(c, c.fields)
14323     }));
14324 };
14325 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14326  * Based on:
14327  * Ext JS Library 1.1.1
14328  * Copyright(c) 2006-2007, Ext JS, LLC.
14329  *
14330  * Originally Released Under LGPL - original licence link has changed is not relivant.
14331  *
14332  * Fork - LGPL
14333  * <script type="text/javascript">
14334  */
14335
14336  
14337 Roo.data.Field = function(config){
14338     if(typeof config == "string"){
14339         config = {name: config};
14340     }
14341     Roo.apply(this, config);
14342     
14343     if(!this.type){
14344         this.type = "auto";
14345     }
14346     
14347     var st = Roo.data.SortTypes;
14348     // named sortTypes are supported, here we look them up
14349     if(typeof this.sortType == "string"){
14350         this.sortType = st[this.sortType];
14351     }
14352     
14353     // set default sortType for strings and dates
14354     if(!this.sortType){
14355         switch(this.type){
14356             case "string":
14357                 this.sortType = st.asUCString;
14358                 break;
14359             case "date":
14360                 this.sortType = st.asDate;
14361                 break;
14362             default:
14363                 this.sortType = st.none;
14364         }
14365     }
14366
14367     // define once
14368     var stripRe = /[\$,%]/g;
14369
14370     // prebuilt conversion function for this field, instead of
14371     // switching every time we're reading a value
14372     if(!this.convert){
14373         var cv, dateFormat = this.dateFormat;
14374         switch(this.type){
14375             case "":
14376             case "auto":
14377             case undefined:
14378                 cv = function(v){ return v; };
14379                 break;
14380             case "string":
14381                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14382                 break;
14383             case "int":
14384                 cv = function(v){
14385                     return v !== undefined && v !== null && v !== '' ?
14386                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14387                     };
14388                 break;
14389             case "float":
14390                 cv = function(v){
14391                     return v !== undefined && v !== null && v !== '' ?
14392                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14393                     };
14394                 break;
14395             case "bool":
14396             case "boolean":
14397                 cv = function(v){ return v === true || v === "true" || v == 1; };
14398                 break;
14399             case "date":
14400                 cv = function(v){
14401                     if(!v){
14402                         return '';
14403                     }
14404                     if(v instanceof Date){
14405                         return v;
14406                     }
14407                     if(dateFormat){
14408                         if(dateFormat == "timestamp"){
14409                             return new Date(v*1000);
14410                         }
14411                         return Date.parseDate(v, dateFormat);
14412                     }
14413                     var parsed = Date.parse(v);
14414                     return parsed ? new Date(parsed) : null;
14415                 };
14416              break;
14417             
14418         }
14419         this.convert = cv;
14420     }
14421 };
14422
14423 Roo.data.Field.prototype = {
14424     dateFormat: null,
14425     defaultValue: "",
14426     mapping: null,
14427     sortType : null,
14428     sortDir : "ASC"
14429 };/*
14430  * Based on:
14431  * Ext JS Library 1.1.1
14432  * Copyright(c) 2006-2007, Ext JS, LLC.
14433  *
14434  * Originally Released Under LGPL - original licence link has changed is not relivant.
14435  *
14436  * Fork - LGPL
14437  * <script type="text/javascript">
14438  */
14439  
14440 // Base class for reading structured data from a data source.  This class is intended to be
14441 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14442
14443 /**
14444  * @class Roo.data.DataReader
14445  * Base class for reading structured data from a data source.  This class is intended to be
14446  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14447  */
14448
14449 Roo.data.DataReader = function(meta, recordType){
14450     
14451     this.meta = meta;
14452     
14453     this.recordType = recordType instanceof Array ? 
14454         Roo.data.Record.create(recordType) : recordType;
14455 };
14456
14457 Roo.data.DataReader.prototype = {
14458     
14459     
14460     readerType : 'Data',
14461      /**
14462      * Create an empty record
14463      * @param {Object} data (optional) - overlay some values
14464      * @return {Roo.data.Record} record created.
14465      */
14466     newRow :  function(d) {
14467         var da =  {};
14468         this.recordType.prototype.fields.each(function(c) {
14469             switch( c.type) {
14470                 case 'int' : da[c.name] = 0; break;
14471                 case 'date' : da[c.name] = new Date(); break;
14472                 case 'float' : da[c.name] = 0.0; break;
14473                 case 'boolean' : da[c.name] = false; break;
14474                 default : da[c.name] = ""; break;
14475             }
14476             
14477         });
14478         return new this.recordType(Roo.apply(da, d));
14479     }
14480     
14481     
14482 };/*
14483  * Based on:
14484  * Ext JS Library 1.1.1
14485  * Copyright(c) 2006-2007, Ext JS, LLC.
14486  *
14487  * Originally Released Under LGPL - original licence link has changed is not relivant.
14488  *
14489  * Fork - LGPL
14490  * <script type="text/javascript">
14491  */
14492
14493 /**
14494  * @class Roo.data.DataProxy
14495  * @extends Roo.data.Observable
14496  * This class is an abstract base class for implementations which provide retrieval of
14497  * unformatted data objects.<br>
14498  * <p>
14499  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14500  * (of the appropriate type which knows how to parse the data object) to provide a block of
14501  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14502  * <p>
14503  * Custom implementations must implement the load method as described in
14504  * {@link Roo.data.HttpProxy#load}.
14505  */
14506 Roo.data.DataProxy = function(){
14507     this.addEvents({
14508         /**
14509          * @event beforeload
14510          * Fires before a network request is made to retrieve a data object.
14511          * @param {Object} This DataProxy object.
14512          * @param {Object} params The params parameter to the load function.
14513          */
14514         beforeload : true,
14515         /**
14516          * @event load
14517          * Fires before the load method's callback is called.
14518          * @param {Object} This DataProxy object.
14519          * @param {Object} o The data object.
14520          * @param {Object} arg The callback argument object passed to the load function.
14521          */
14522         load : true,
14523         /**
14524          * @event loadexception
14525          * Fires if an Exception occurs during data retrieval.
14526          * @param {Object} This DataProxy object.
14527          * @param {Object} o The data object.
14528          * @param {Object} arg The callback argument object passed to the load function.
14529          * @param {Object} e The Exception.
14530          */
14531         loadexception : true
14532     });
14533     Roo.data.DataProxy.superclass.constructor.call(this);
14534 };
14535
14536 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14537
14538     /**
14539      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14540      */
14541 /*
14542  * Based on:
14543  * Ext JS Library 1.1.1
14544  * Copyright(c) 2006-2007, Ext JS, LLC.
14545  *
14546  * Originally Released Under LGPL - original licence link has changed is not relivant.
14547  *
14548  * Fork - LGPL
14549  * <script type="text/javascript">
14550  */
14551 /**
14552  * @class Roo.data.MemoryProxy
14553  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14554  * to the Reader when its load method is called.
14555  * @constructor
14556  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14557  */
14558 Roo.data.MemoryProxy = function(data){
14559     if (data.data) {
14560         data = data.data;
14561     }
14562     Roo.data.MemoryProxy.superclass.constructor.call(this);
14563     this.data = data;
14564 };
14565
14566 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14567     
14568     /**
14569      * Load data from the requested source (in this case an in-memory
14570      * data object passed to the constructor), read the data object into
14571      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14572      * process that block using the passed callback.
14573      * @param {Object} params This parameter is not used by the MemoryProxy class.
14574      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14575      * object into a block of Roo.data.Records.
14576      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14577      * The function must be passed <ul>
14578      * <li>The Record block object</li>
14579      * <li>The "arg" argument from the load function</li>
14580      * <li>A boolean success indicator</li>
14581      * </ul>
14582      * @param {Object} scope The scope in which to call the callback
14583      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14584      */
14585     load : function(params, reader, callback, scope, arg){
14586         params = params || {};
14587         var result;
14588         try {
14589             result = reader.readRecords(params.data ? params.data :this.data);
14590         }catch(e){
14591             this.fireEvent("loadexception", this, arg, null, e);
14592             callback.call(scope, null, arg, false);
14593             return;
14594         }
14595         callback.call(scope, result, arg, true);
14596     },
14597     
14598     // private
14599     update : function(params, records){
14600         
14601     }
14602 });/*
14603  * Based on:
14604  * Ext JS Library 1.1.1
14605  * Copyright(c) 2006-2007, Ext JS, LLC.
14606  *
14607  * Originally Released Under LGPL - original licence link has changed is not relivant.
14608  *
14609  * Fork - LGPL
14610  * <script type="text/javascript">
14611  */
14612 /**
14613  * @class Roo.data.HttpProxy
14614  * @extends Roo.data.DataProxy
14615  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14616  * configured to reference a certain URL.<br><br>
14617  * <p>
14618  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14619  * from which the running page was served.<br><br>
14620  * <p>
14621  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14622  * <p>
14623  * Be aware that to enable the browser to parse an XML document, the server must set
14624  * the Content-Type header in the HTTP response to "text/xml".
14625  * @constructor
14626  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14627  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14628  * will be used to make the request.
14629  */
14630 Roo.data.HttpProxy = function(conn){
14631     Roo.data.HttpProxy.superclass.constructor.call(this);
14632     // is conn a conn config or a real conn?
14633     this.conn = conn;
14634     this.useAjax = !conn || !conn.events;
14635   
14636 };
14637
14638 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14639     // thse are take from connection...
14640     
14641     /**
14642      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14643      */
14644     /**
14645      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14646      * extra parameters to each request made by this object. (defaults to undefined)
14647      */
14648     /**
14649      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14650      *  to each request made by this object. (defaults to undefined)
14651      */
14652     /**
14653      * @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)
14654      */
14655     /**
14656      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14657      */
14658      /**
14659      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14660      * @type Boolean
14661      */
14662   
14663
14664     /**
14665      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14666      * @type Boolean
14667      */
14668     /**
14669      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14670      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14671      * a finer-grained basis than the DataProxy events.
14672      */
14673     getConnection : function(){
14674         return this.useAjax ? Roo.Ajax : this.conn;
14675     },
14676
14677     /**
14678      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14679      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14680      * process that block using the passed callback.
14681      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14682      * for the request to the remote server.
14683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14684      * object into a block of Roo.data.Records.
14685      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14686      * The function must be passed <ul>
14687      * <li>The Record block object</li>
14688      * <li>The "arg" argument from the load function</li>
14689      * <li>A boolean success indicator</li>
14690      * </ul>
14691      * @param {Object} scope The scope in which to call the callback
14692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14693      */
14694     load : function(params, reader, callback, scope, arg){
14695         if(this.fireEvent("beforeload", this, params) !== false){
14696             var  o = {
14697                 params : params || {},
14698                 request: {
14699                     callback : callback,
14700                     scope : scope,
14701                     arg : arg
14702                 },
14703                 reader: reader,
14704                 callback : this.loadResponse,
14705                 scope: this
14706             };
14707             if(this.useAjax){
14708                 Roo.applyIf(o, this.conn);
14709                 if(this.activeRequest){
14710                     Roo.Ajax.abort(this.activeRequest);
14711                 }
14712                 this.activeRequest = Roo.Ajax.request(o);
14713             }else{
14714                 this.conn.request(o);
14715             }
14716         }else{
14717             callback.call(scope||this, null, arg, false);
14718         }
14719     },
14720
14721     // private
14722     loadResponse : function(o, success, response){
14723         delete this.activeRequest;
14724         if(!success){
14725             this.fireEvent("loadexception", this, o, response);
14726             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14727             return;
14728         }
14729         var result;
14730         try {
14731             result = o.reader.read(response);
14732         }catch(e){
14733             this.fireEvent("loadexception", this, o, response, e);
14734             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14735             return;
14736         }
14737         
14738         this.fireEvent("load", this, o, o.request.arg);
14739         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14740     },
14741
14742     // private
14743     update : function(dataSet){
14744
14745     },
14746
14747     // private
14748     updateResponse : function(dataSet){
14749
14750     }
14751 });/*
14752  * Based on:
14753  * Ext JS Library 1.1.1
14754  * Copyright(c) 2006-2007, Ext JS, LLC.
14755  *
14756  * Originally Released Under LGPL - original licence link has changed is not relivant.
14757  *
14758  * Fork - LGPL
14759  * <script type="text/javascript">
14760  */
14761
14762 /**
14763  * @class Roo.data.ScriptTagProxy
14764  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14765  * other than the originating domain of the running page.<br><br>
14766  * <p>
14767  * <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
14768  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14769  * <p>
14770  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14771  * source code that is used as the source inside a &lt;script> tag.<br><br>
14772  * <p>
14773  * In order for the browser to process the returned data, the server must wrap the data object
14774  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14775  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14776  * depending on whether the callback name was passed:
14777  * <p>
14778  * <pre><code>
14779 boolean scriptTag = false;
14780 String cb = request.getParameter("callback");
14781 if (cb != null) {
14782     scriptTag = true;
14783     response.setContentType("text/javascript");
14784 } else {
14785     response.setContentType("application/x-json");
14786 }
14787 Writer out = response.getWriter();
14788 if (scriptTag) {
14789     out.write(cb + "(");
14790 }
14791 out.print(dataBlock.toJsonString());
14792 if (scriptTag) {
14793     out.write(");");
14794 }
14795 </pre></code>
14796  *
14797  * @constructor
14798  * @param {Object} config A configuration object.
14799  */
14800 Roo.data.ScriptTagProxy = function(config){
14801     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14802     Roo.apply(this, config);
14803     this.head = document.getElementsByTagName("head")[0];
14804 };
14805
14806 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14807
14808 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14809     /**
14810      * @cfg {String} url The URL from which to request the data object.
14811      */
14812     /**
14813      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14814      */
14815     timeout : 30000,
14816     /**
14817      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14818      * the server the name of the callback function set up by the load call to process the returned data object.
14819      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14820      * javascript output which calls this named function passing the data object as its only parameter.
14821      */
14822     callbackParam : "callback",
14823     /**
14824      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14825      * name to the request.
14826      */
14827     nocache : true,
14828
14829     /**
14830      * Load data from the configured URL, read the data object into
14831      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14832      * process that block using the passed callback.
14833      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14834      * for the request to the remote server.
14835      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14836      * object into a block of Roo.data.Records.
14837      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14838      * The function must be passed <ul>
14839      * <li>The Record block object</li>
14840      * <li>The "arg" argument from the load function</li>
14841      * <li>A boolean success indicator</li>
14842      * </ul>
14843      * @param {Object} scope The scope in which to call the callback
14844      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14845      */
14846     load : function(params, reader, callback, scope, arg){
14847         if(this.fireEvent("beforeload", this, params) !== false){
14848
14849             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14850
14851             var url = this.url;
14852             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14853             if(this.nocache){
14854                 url += "&_dc=" + (new Date().getTime());
14855             }
14856             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14857             var trans = {
14858                 id : transId,
14859                 cb : "stcCallback"+transId,
14860                 scriptId : "stcScript"+transId,
14861                 params : params,
14862                 arg : arg,
14863                 url : url,
14864                 callback : callback,
14865                 scope : scope,
14866                 reader : reader
14867             };
14868             var conn = this;
14869
14870             window[trans.cb] = function(o){
14871                 conn.handleResponse(o, trans);
14872             };
14873
14874             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14875
14876             if(this.autoAbort !== false){
14877                 this.abort();
14878             }
14879
14880             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14881
14882             var script = document.createElement("script");
14883             script.setAttribute("src", url);
14884             script.setAttribute("type", "text/javascript");
14885             script.setAttribute("id", trans.scriptId);
14886             this.head.appendChild(script);
14887
14888             this.trans = trans;
14889         }else{
14890             callback.call(scope||this, null, arg, false);
14891         }
14892     },
14893
14894     // private
14895     isLoading : function(){
14896         return this.trans ? true : false;
14897     },
14898
14899     /**
14900      * Abort the current server request.
14901      */
14902     abort : function(){
14903         if(this.isLoading()){
14904             this.destroyTrans(this.trans);
14905         }
14906     },
14907
14908     // private
14909     destroyTrans : function(trans, isLoaded){
14910         this.head.removeChild(document.getElementById(trans.scriptId));
14911         clearTimeout(trans.timeoutId);
14912         if(isLoaded){
14913             window[trans.cb] = undefined;
14914             try{
14915                 delete window[trans.cb];
14916             }catch(e){}
14917         }else{
14918             // if hasn't been loaded, wait for load to remove it to prevent script error
14919             window[trans.cb] = function(){
14920                 window[trans.cb] = undefined;
14921                 try{
14922                     delete window[trans.cb];
14923                 }catch(e){}
14924             };
14925         }
14926     },
14927
14928     // private
14929     handleResponse : function(o, trans){
14930         this.trans = false;
14931         this.destroyTrans(trans, true);
14932         var result;
14933         try {
14934             result = trans.reader.readRecords(o);
14935         }catch(e){
14936             this.fireEvent("loadexception", this, o, trans.arg, e);
14937             trans.callback.call(trans.scope||window, null, trans.arg, false);
14938             return;
14939         }
14940         this.fireEvent("load", this, o, trans.arg);
14941         trans.callback.call(trans.scope||window, result, trans.arg, true);
14942     },
14943
14944     // private
14945     handleFailure : function(trans){
14946         this.trans = false;
14947         this.destroyTrans(trans, false);
14948         this.fireEvent("loadexception", this, null, trans.arg);
14949         trans.callback.call(trans.scope||window, null, trans.arg, false);
14950     }
14951 });/*
14952  * Based on:
14953  * Ext JS Library 1.1.1
14954  * Copyright(c) 2006-2007, Ext JS, LLC.
14955  *
14956  * Originally Released Under LGPL - original licence link has changed is not relivant.
14957  *
14958  * Fork - LGPL
14959  * <script type="text/javascript">
14960  */
14961
14962 /**
14963  * @class Roo.data.JsonReader
14964  * @extends Roo.data.DataReader
14965  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14966  * based on mappings in a provided Roo.data.Record constructor.
14967  * 
14968  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14969  * in the reply previously. 
14970  * 
14971  * <p>
14972  * Example code:
14973  * <pre><code>
14974 var RecordDef = Roo.data.Record.create([
14975     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14976     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14977 ]);
14978 var myReader = new Roo.data.JsonReader({
14979     totalProperty: "results",    // The property which contains the total dataset size (optional)
14980     root: "rows",                // The property which contains an Array of row objects
14981     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14982 }, RecordDef);
14983 </code></pre>
14984  * <p>
14985  * This would consume a JSON file like this:
14986  * <pre><code>
14987 { 'results': 2, 'rows': [
14988     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14989     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14990 }
14991 </code></pre>
14992  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14993  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14994  * paged from the remote server.
14995  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14996  * @cfg {String} root name of the property which contains the Array of row objects.
14997  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14998  * @cfg {Array} fields Array of field definition objects
14999  * @constructor
15000  * Create a new JsonReader
15001  * @param {Object} meta Metadata configuration options
15002  * @param {Object} recordType Either an Array of field definition objects,
15003  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15004  */
15005 Roo.data.JsonReader = function(meta, recordType){
15006     
15007     meta = meta || {};
15008     // set some defaults:
15009     Roo.applyIf(meta, {
15010         totalProperty: 'total',
15011         successProperty : 'success',
15012         root : 'data',
15013         id : 'id'
15014     });
15015     
15016     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15017 };
15018 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15019     
15020     readerType : 'Json',
15021     
15022     /**
15023      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15024      * Used by Store query builder to append _requestMeta to params.
15025      * 
15026      */
15027     metaFromRemote : false,
15028     /**
15029      * This method is only used by a DataProxy which has retrieved data from a remote server.
15030      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15031      * @return {Object} data A data block which is used by an Roo.data.Store object as
15032      * a cache of Roo.data.Records.
15033      */
15034     read : function(response){
15035         var json = response.responseText;
15036        
15037         var o = /* eval:var:o */ eval("("+json+")");
15038         if(!o) {
15039             throw {message: "JsonReader.read: Json object not found"};
15040         }
15041         
15042         if(o.metaData){
15043             
15044             delete this.ef;
15045             this.metaFromRemote = true;
15046             this.meta = o.metaData;
15047             this.recordType = Roo.data.Record.create(o.metaData.fields);
15048             this.onMetaChange(this.meta, this.recordType, o);
15049         }
15050         return this.readRecords(o);
15051     },
15052
15053     // private function a store will implement
15054     onMetaChange : function(meta, recordType, o){
15055
15056     },
15057
15058     /**
15059          * @ignore
15060          */
15061     simpleAccess: function(obj, subsc) {
15062         return obj[subsc];
15063     },
15064
15065         /**
15066          * @ignore
15067          */
15068     getJsonAccessor: function(){
15069         var re = /[\[\.]/;
15070         return function(expr) {
15071             try {
15072                 return(re.test(expr))
15073                     ? new Function("obj", "return obj." + expr)
15074                     : function(obj){
15075                         return obj[expr];
15076                     };
15077             } catch(e){}
15078             return Roo.emptyFn;
15079         };
15080     }(),
15081
15082     /**
15083      * Create a data block containing Roo.data.Records from an XML document.
15084      * @param {Object} o An object which contains an Array of row objects in the property specified
15085      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15086      * which contains the total size of the dataset.
15087      * @return {Object} data A data block which is used by an Roo.data.Store object as
15088      * a cache of Roo.data.Records.
15089      */
15090     readRecords : function(o){
15091         /**
15092          * After any data loads, the raw JSON data is available for further custom processing.
15093          * @type Object
15094          */
15095         this.o = o;
15096         var s = this.meta, Record = this.recordType,
15097             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15098
15099 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15100         if (!this.ef) {
15101             if(s.totalProperty) {
15102                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15103                 }
15104                 if(s.successProperty) {
15105                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15106                 }
15107                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15108                 if (s.id) {
15109                         var g = this.getJsonAccessor(s.id);
15110                         this.getId = function(rec) {
15111                                 var r = g(rec);  
15112                                 return (r === undefined || r === "") ? null : r;
15113                         };
15114                 } else {
15115                         this.getId = function(){return null;};
15116                 }
15117             this.ef = [];
15118             for(var jj = 0; jj < fl; jj++){
15119                 f = fi[jj];
15120                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15121                 this.ef[jj] = this.getJsonAccessor(map);
15122             }
15123         }
15124
15125         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15126         if(s.totalProperty){
15127             var vt = parseInt(this.getTotal(o), 10);
15128             if(!isNaN(vt)){
15129                 totalRecords = vt;
15130             }
15131         }
15132         if(s.successProperty){
15133             var vs = this.getSuccess(o);
15134             if(vs === false || vs === 'false'){
15135                 success = false;
15136             }
15137         }
15138         var records = [];
15139         for(var i = 0; i < c; i++){
15140                 var n = root[i];
15141             var values = {};
15142             var id = this.getId(n);
15143             for(var j = 0; j < fl; j++){
15144                 f = fi[j];
15145             var v = this.ef[j](n);
15146             if (!f.convert) {
15147                 Roo.log('missing convert for ' + f.name);
15148                 Roo.log(f);
15149                 continue;
15150             }
15151             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15152             }
15153             var record = new Record(values, id);
15154             record.json = n;
15155             records[i] = record;
15156         }
15157         return {
15158             raw : o,
15159             success : success,
15160             records : records,
15161             totalRecords : totalRecords
15162         };
15163     },
15164     // used when loading children.. @see loadDataFromChildren
15165     toLoadData: function(rec)
15166     {
15167         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15168         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15169         return { data : data, total : data.length };
15170         
15171     }
15172 });/*
15173  * Based on:
15174  * Ext JS Library 1.1.1
15175  * Copyright(c) 2006-2007, Ext JS, LLC.
15176  *
15177  * Originally Released Under LGPL - original licence link has changed is not relivant.
15178  *
15179  * Fork - LGPL
15180  * <script type="text/javascript">
15181  */
15182
15183 /**
15184  * @class Roo.data.ArrayReader
15185  * @extends Roo.data.DataReader
15186  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15187  * Each element of that Array represents a row of data fields. The
15188  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15189  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15190  * <p>
15191  * Example code:.
15192  * <pre><code>
15193 var RecordDef = Roo.data.Record.create([
15194     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15195     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15196 ]);
15197 var myReader = new Roo.data.ArrayReader({
15198     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15199 }, RecordDef);
15200 </code></pre>
15201  * <p>
15202  * This would consume an Array like this:
15203  * <pre><code>
15204 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15205   </code></pre>
15206  
15207  * @constructor
15208  * Create a new JsonReader
15209  * @param {Object} meta Metadata configuration options.
15210  * @param {Object|Array} recordType Either an Array of field definition objects
15211  * 
15212  * @cfg {Array} fields Array of field definition objects
15213  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15214  * as specified to {@link Roo.data.Record#create},
15215  * or an {@link Roo.data.Record} object
15216  *
15217  * 
15218  * created using {@link Roo.data.Record#create}.
15219  */
15220 Roo.data.ArrayReader = function(meta, recordType)
15221 {    
15222     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15223 };
15224
15225 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15226     
15227       /**
15228      * Create a data block containing Roo.data.Records from an XML document.
15229      * @param {Object} o An Array of row objects which represents the dataset.
15230      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15231      * a cache of Roo.data.Records.
15232      */
15233     readRecords : function(o)
15234     {
15235         var sid = this.meta ? this.meta.id : null;
15236         var recordType = this.recordType, fields = recordType.prototype.fields;
15237         var records = [];
15238         var root = o;
15239         for(var i = 0; i < root.length; i++){
15240             var n = root[i];
15241             var values = {};
15242             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15243             for(var j = 0, jlen = fields.length; j < jlen; j++){
15244                 var f = fields.items[j];
15245                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15246                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15247                 v = f.convert(v);
15248                 values[f.name] = v;
15249             }
15250             var record = new recordType(values, id);
15251             record.json = n;
15252             records[records.length] = record;
15253         }
15254         return {
15255             records : records,
15256             totalRecords : records.length
15257         };
15258     },
15259     // used when loading children.. @see loadDataFromChildren
15260     toLoadData: function(rec)
15261     {
15262         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15263         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15264         
15265     }
15266     
15267     
15268 });/*
15269  * - LGPL
15270  * * 
15271  */
15272
15273 /**
15274  * @class Roo.bootstrap.ComboBox
15275  * @extends Roo.bootstrap.TriggerField
15276  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15277  * @cfg {Boolean} append (true|false) default false
15278  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15279  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15280  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15281  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15282  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15283  * @cfg {Boolean} animate default true
15284  * @cfg {Boolean} emptyResultText only for touch device
15285  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15286  * @cfg {String} emptyTitle default ''
15287  * @cfg {Number} width fixed with? experimental
15288  * @constructor
15289  * Create a new ComboBox.
15290  * @param {Object} config Configuration options
15291  */
15292 Roo.bootstrap.ComboBox = function(config){
15293     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15294     this.addEvents({
15295         /**
15296          * @event expand
15297          * Fires when the dropdown list is expanded
15298         * @param {Roo.bootstrap.ComboBox} combo This combo box
15299         */
15300         'expand' : true,
15301         /**
15302          * @event collapse
15303          * Fires when the dropdown list is collapsed
15304         * @param {Roo.bootstrap.ComboBox} combo This combo box
15305         */
15306         'collapse' : true,
15307         /**
15308          * @event beforeselect
15309          * Fires before a list item is selected. Return false to cancel the selection.
15310         * @param {Roo.bootstrap.ComboBox} combo This combo box
15311         * @param {Roo.data.Record} record The data record returned from the underlying store
15312         * @param {Number} index The index of the selected item in the dropdown list
15313         */
15314         'beforeselect' : true,
15315         /**
15316          * @event select
15317          * Fires when a list item is selected
15318         * @param {Roo.bootstrap.ComboBox} combo This combo box
15319         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15320         * @param {Number} index The index of the selected item in the dropdown list
15321         */
15322         'select' : true,
15323         /**
15324          * @event beforequery
15325          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15326          * The event object passed has these properties:
15327         * @param {Roo.bootstrap.ComboBox} combo This combo box
15328         * @param {String} query The query
15329         * @param {Boolean} forceAll true to force "all" query
15330         * @param {Boolean} cancel true to cancel the query
15331         * @param {Object} e The query event object
15332         */
15333         'beforequery': true,
15334          /**
15335          * @event add
15336          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15337         * @param {Roo.bootstrap.ComboBox} combo This combo box
15338         */
15339         'add' : true,
15340         /**
15341          * @event edit
15342          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15343         * @param {Roo.bootstrap.ComboBox} combo This combo box
15344         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15345         */
15346         'edit' : true,
15347         /**
15348          * @event remove
15349          * Fires when the remove value from the combobox array
15350         * @param {Roo.bootstrap.ComboBox} combo This combo box
15351         */
15352         'remove' : true,
15353         /**
15354          * @event afterremove
15355          * Fires when the remove value from the combobox array
15356         * @param {Roo.bootstrap.ComboBox} combo This combo box
15357         */
15358         'afterremove' : true,
15359         /**
15360          * @event specialfilter
15361          * Fires when specialfilter
15362             * @param {Roo.bootstrap.ComboBox} combo This combo box
15363             */
15364         'specialfilter' : true,
15365         /**
15366          * @event tick
15367          * Fires when tick the element
15368             * @param {Roo.bootstrap.ComboBox} combo This combo box
15369             */
15370         'tick' : true,
15371         /**
15372          * @event touchviewdisplay
15373          * Fires when touch view require special display (default is using displayField)
15374             * @param {Roo.bootstrap.ComboBox} combo This combo box
15375             * @param {Object} cfg set html .
15376             */
15377         'touchviewdisplay' : true
15378         
15379     });
15380     
15381     this.item = [];
15382     this.tickItems = [];
15383     
15384     this.selectedIndex = -1;
15385     if(this.mode == 'local'){
15386         if(config.queryDelay === undefined){
15387             this.queryDelay = 10;
15388         }
15389         if(config.minChars === undefined){
15390             this.minChars = 0;
15391         }
15392     }
15393 };
15394
15395 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15396      
15397     /**
15398      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15399      * rendering into an Roo.Editor, defaults to false)
15400      */
15401     /**
15402      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15403      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15404      */
15405     /**
15406      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15407      */
15408     /**
15409      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15410      * the dropdown list (defaults to undefined, with no header element)
15411      */
15412
15413      /**
15414      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15415      */
15416      
15417      /**
15418      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15419      */
15420     listWidth: undefined,
15421     /**
15422      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15423      * mode = 'remote' or 'text' if mode = 'local')
15424      */
15425     displayField: undefined,
15426     
15427     /**
15428      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15429      * mode = 'remote' or 'value' if mode = 'local'). 
15430      * Note: use of a valueField requires the user make a selection
15431      * in order for a value to be mapped.
15432      */
15433     valueField: undefined,
15434     /**
15435      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15436      */
15437     modalTitle : '',
15438     
15439     /**
15440      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15441      * field's data value (defaults to the underlying DOM element's name)
15442      */
15443     hiddenName: undefined,
15444     /**
15445      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15446      */
15447     listClass: '',
15448     /**
15449      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15450      */
15451     selectedClass: 'active',
15452     
15453     /**
15454      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15455      */
15456     shadow:'sides',
15457     /**
15458      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15459      * anchor positions (defaults to 'tl-bl')
15460      */
15461     listAlign: 'tl-bl?',
15462     /**
15463      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15464      */
15465     maxHeight: 300,
15466     /**
15467      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15468      * query specified by the allQuery config option (defaults to 'query')
15469      */
15470     triggerAction: 'query',
15471     /**
15472      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15473      * (defaults to 4, does not apply if editable = false)
15474      */
15475     minChars : 4,
15476     /**
15477      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15478      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15479      */
15480     typeAhead: false,
15481     /**
15482      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15483      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15484      */
15485     queryDelay: 500,
15486     /**
15487      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15488      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15489      */
15490     pageSize: 0,
15491     /**
15492      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15493      * when editable = true (defaults to false)
15494      */
15495     selectOnFocus:false,
15496     /**
15497      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15498      */
15499     queryParam: 'query',
15500     /**
15501      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15502      * when mode = 'remote' (defaults to 'Loading...')
15503      */
15504     loadingText: 'Loading...',
15505     /**
15506      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15507      */
15508     resizable: false,
15509     /**
15510      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15511      */
15512     handleHeight : 8,
15513     /**
15514      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15515      * traditional select (defaults to true)
15516      */
15517     editable: true,
15518     /**
15519      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15520      */
15521     allQuery: '',
15522     /**
15523      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15524      */
15525     mode: 'remote',
15526     /**
15527      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15528      * listWidth has a higher value)
15529      */
15530     minListWidth : 70,
15531     /**
15532      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15533      * allow the user to set arbitrary text into the field (defaults to false)
15534      */
15535     forceSelection:false,
15536     /**
15537      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15538      * if typeAhead = true (defaults to 250)
15539      */
15540     typeAheadDelay : 250,
15541     /**
15542      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15543      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15544      */
15545     valueNotFoundText : undefined,
15546     /**
15547      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15548      */
15549     blockFocus : false,
15550     
15551     /**
15552      * @cfg {Boolean} disableClear Disable showing of clear button.
15553      */
15554     disableClear : false,
15555     /**
15556      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15557      */
15558     alwaysQuery : false,
15559     
15560     /**
15561      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15562      */
15563     multiple : false,
15564     
15565     /**
15566      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15567      */
15568     invalidClass : "has-warning",
15569     
15570     /**
15571      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15572      */
15573     validClass : "has-success",
15574     
15575     /**
15576      * @cfg {Boolean} specialFilter (true|false) special filter default false
15577      */
15578     specialFilter : false,
15579     
15580     /**
15581      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15582      */
15583     mobileTouchView : true,
15584     
15585     /**
15586      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15587      */
15588     useNativeIOS : false,
15589     
15590     /**
15591      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15592      */
15593     mobile_restrict_height : false,
15594     
15595     ios_options : false,
15596     
15597     //private
15598     addicon : false,
15599     editicon: false,
15600     
15601     page: 0,
15602     hasQuery: false,
15603     append: false,
15604     loadNext: false,
15605     autoFocus : true,
15606     tickable : false,
15607     btnPosition : 'right',
15608     triggerList : true,
15609     showToggleBtn : true,
15610     animate : true,
15611     emptyResultText: 'Empty',
15612     triggerText : 'Select',
15613     emptyTitle : '',
15614     width : false,
15615     
15616     // element that contains real text value.. (when hidden is used..)
15617     
15618     getAutoCreate : function()
15619     {   
15620         var cfg = false;
15621         //render
15622         /*
15623          * Render classic select for iso
15624          */
15625         
15626         if(Roo.isIOS && this.useNativeIOS){
15627             cfg = this.getAutoCreateNativeIOS();
15628             return cfg;
15629         }
15630         
15631         /*
15632          * Touch Devices
15633          */
15634         
15635         if(Roo.isTouch && this.mobileTouchView){
15636             cfg = this.getAutoCreateTouchView();
15637             return cfg;;
15638         }
15639         
15640         /*
15641          *  Normal ComboBox
15642          */
15643         if(!this.tickable){
15644             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15645             return cfg;
15646         }
15647         
15648         /*
15649          *  ComboBox with tickable selections
15650          */
15651              
15652         var align = this.labelAlign || this.parentLabelAlign();
15653         
15654         cfg = {
15655             cls : 'form-group roo-combobox-tickable' //input-group
15656         };
15657         
15658         var btn_text_select = '';
15659         var btn_text_done = '';
15660         var btn_text_cancel = '';
15661         
15662         if (this.btn_text_show) {
15663             btn_text_select = 'Select';
15664             btn_text_done = 'Done';
15665             btn_text_cancel = 'Cancel'; 
15666         }
15667         
15668         var buttons = {
15669             tag : 'div',
15670             cls : 'tickable-buttons',
15671             cn : [
15672                 {
15673                     tag : 'button',
15674                     type : 'button',
15675                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15676                     //html : this.triggerText
15677                     html: btn_text_select
15678                 },
15679                 {
15680                     tag : 'button',
15681                     type : 'button',
15682                     name : 'ok',
15683                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15684                     //html : 'Done'
15685                     html: btn_text_done
15686                 },
15687                 {
15688                     tag : 'button',
15689                     type : 'button',
15690                     name : 'cancel',
15691                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15692                     //html : 'Cancel'
15693                     html: btn_text_cancel
15694                 }
15695             ]
15696         };
15697         
15698         if(this.editable){
15699             buttons.cn.unshift({
15700                 tag: 'input',
15701                 cls: 'roo-select2-search-field-input'
15702             });
15703         }
15704         
15705         var _this = this;
15706         
15707         Roo.each(buttons.cn, function(c){
15708             if (_this.size) {
15709                 c.cls += ' btn-' + _this.size;
15710             }
15711
15712             if (_this.disabled) {
15713                 c.disabled = true;
15714             }
15715         });
15716         
15717         var box = {
15718             tag: 'div',
15719             style : 'display: contents',
15720             cn: [
15721                 {
15722                     tag: 'input',
15723                     type : 'hidden',
15724                     cls: 'form-hidden-field'
15725                 },
15726                 {
15727                     tag: 'ul',
15728                     cls: 'roo-select2-choices',
15729                     cn:[
15730                         {
15731                             tag: 'li',
15732                             cls: 'roo-select2-search-field',
15733                             cn: [
15734                                 buttons
15735                             ]
15736                         }
15737                     ]
15738                 }
15739             ]
15740         };
15741         
15742         var combobox = {
15743             cls: 'roo-select2-container input-group roo-select2-container-multi',
15744             cn: [
15745                 
15746                 box
15747 //                {
15748 //                    tag: 'ul',
15749 //                    cls: 'typeahead typeahead-long dropdown-menu',
15750 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15751 //                }
15752             ]
15753         };
15754         
15755         if(this.hasFeedback && !this.allowBlank){
15756             
15757             var feedback = {
15758                 tag: 'span',
15759                 cls: 'glyphicon form-control-feedback'
15760             };
15761
15762             combobox.cn.push(feedback);
15763         }
15764         
15765         
15766         
15767         var indicator = {
15768             tag : 'i',
15769             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15770             tooltip : 'This field is required'
15771         };
15772         if (Roo.bootstrap.version == 4) {
15773             indicator = {
15774                 tag : 'i',
15775                 style : 'display:none'
15776             };
15777         }
15778         if (align ==='left' && this.fieldLabel.length) {
15779             
15780             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15781             
15782             cfg.cn = [
15783                 indicator,
15784                 {
15785                     tag: 'label',
15786                     'for' :  id,
15787                     cls : 'control-label col-form-label',
15788                     html : this.fieldLabel
15789
15790                 },
15791                 {
15792                     cls : "", 
15793                     cn: [
15794                         combobox
15795                     ]
15796                 }
15797
15798             ];
15799             
15800             var labelCfg = cfg.cn[1];
15801             var contentCfg = cfg.cn[2];
15802             
15803
15804             if(this.indicatorpos == 'right'){
15805                 
15806                 cfg.cn = [
15807                     {
15808                         tag: 'label',
15809                         'for' :  id,
15810                         cls : 'control-label col-form-label',
15811                         cn : [
15812                             {
15813                                 tag : 'span',
15814                                 html : this.fieldLabel
15815                             },
15816                             indicator
15817                         ]
15818                     },
15819                     {
15820                         cls : "",
15821                         cn: [
15822                             combobox
15823                         ]
15824                     }
15825
15826                 ];
15827                 
15828                 
15829                 
15830                 labelCfg = cfg.cn[0];
15831                 contentCfg = cfg.cn[1];
15832             
15833             }
15834             
15835             if(this.labelWidth > 12){
15836                 labelCfg.style = "width: " + this.labelWidth + 'px';
15837             }
15838             if(this.width * 1 > 0){
15839                 contentCfg.style = "width: " + this.width + 'px';
15840             }
15841             if(this.labelWidth < 13 && this.labelmd == 0){
15842                 this.labelmd = this.labelWidth;
15843             }
15844             
15845             if(this.labellg > 0){
15846                 labelCfg.cls += ' col-lg-' + this.labellg;
15847                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15848             }
15849             
15850             if(this.labelmd > 0){
15851                 labelCfg.cls += ' col-md-' + this.labelmd;
15852                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15853             }
15854             
15855             if(this.labelsm > 0){
15856                 labelCfg.cls += ' col-sm-' + this.labelsm;
15857                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15858             }
15859             
15860             if(this.labelxs > 0){
15861                 labelCfg.cls += ' col-xs-' + this.labelxs;
15862                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15863             }
15864                 
15865                 
15866         } else if ( this.fieldLabel.length) {
15867 //                Roo.log(" label");
15868                  cfg.cn = [
15869                    indicator,
15870                     {
15871                         tag: 'label',
15872                         //cls : 'input-group-addon',
15873                         html : this.fieldLabel
15874                     },
15875                     combobox
15876                 ];
15877                 
15878                 if(this.indicatorpos == 'right'){
15879                     cfg.cn = [
15880                         {
15881                             tag: 'label',
15882                             //cls : 'input-group-addon',
15883                             html : this.fieldLabel
15884                         },
15885                         indicator,
15886                         combobox
15887                     ];
15888                     
15889                 }
15890
15891         } else {
15892             
15893 //                Roo.log(" no label && no align");
15894                 cfg = combobox
15895                      
15896                 
15897         }
15898          
15899         var settings=this;
15900         ['xs','sm','md','lg'].map(function(size){
15901             if (settings[size]) {
15902                 cfg.cls += ' col-' + size + '-' + settings[size];
15903             }
15904         });
15905         
15906         return cfg;
15907         
15908     },
15909     
15910     _initEventsCalled : false,
15911     
15912     // private
15913     initEvents: function()
15914     {   
15915         if (this._initEventsCalled) { // as we call render... prevent looping...
15916             return;
15917         }
15918         this._initEventsCalled = true;
15919         
15920         if (!this.store) {
15921             throw "can not find store for combo";
15922         }
15923         
15924         this.indicator = this.indicatorEl();
15925         
15926         this.store = Roo.factory(this.store, Roo.data);
15927         this.store.parent = this;
15928         
15929         // if we are building from html. then this element is so complex, that we can not really
15930         // use the rendered HTML.
15931         // so we have to trash and replace the previous code.
15932         if (Roo.XComponent.build_from_html) {
15933             // remove this element....
15934             var e = this.el.dom, k=0;
15935             while (e ) { e = e.previousSibling;  ++k;}
15936
15937             this.el.remove();
15938             
15939             this.el=false;
15940             this.rendered = false;
15941             
15942             this.render(this.parent().getChildContainer(true), k);
15943         }
15944         
15945         if(Roo.isIOS && this.useNativeIOS){
15946             this.initIOSView();
15947             return;
15948         }
15949         
15950         /*
15951          * Touch Devices
15952          */
15953         
15954         if(Roo.isTouch && this.mobileTouchView){
15955             this.initTouchView();
15956             return;
15957         }
15958         
15959         if(this.tickable){
15960             this.initTickableEvents();
15961             return;
15962         }
15963         
15964         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15965         
15966         if(this.hiddenName){
15967             
15968             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15969             
15970             this.hiddenField.dom.value =
15971                 this.hiddenValue !== undefined ? this.hiddenValue :
15972                 this.value !== undefined ? this.value : '';
15973
15974             // prevent input submission
15975             this.el.dom.removeAttribute('name');
15976             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15977              
15978              
15979         }
15980         //if(Roo.isGecko){
15981         //    this.el.dom.setAttribute('autocomplete', 'off');
15982         //}
15983         
15984         var cls = 'x-combo-list';
15985         
15986         //this.list = new Roo.Layer({
15987         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15988         //});
15989         
15990         var _this = this;
15991         
15992         (function(){
15993             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15994             _this.list.setWidth(lw);
15995         }).defer(100);
15996         
15997         this.list.on('mouseover', this.onViewOver, this);
15998         this.list.on('mousemove', this.onViewMove, this);
15999         this.list.on('scroll', this.onViewScroll, this);
16000         
16001         /*
16002         this.list.swallowEvent('mousewheel');
16003         this.assetHeight = 0;
16004
16005         if(this.title){
16006             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16007             this.assetHeight += this.header.getHeight();
16008         }
16009
16010         this.innerList = this.list.createChild({cls:cls+'-inner'});
16011         this.innerList.on('mouseover', this.onViewOver, this);
16012         this.innerList.on('mousemove', this.onViewMove, this);
16013         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16014         
16015         if(this.allowBlank && !this.pageSize && !this.disableClear){
16016             this.footer = this.list.createChild({cls:cls+'-ft'});
16017             this.pageTb = new Roo.Toolbar(this.footer);
16018            
16019         }
16020         if(this.pageSize){
16021             this.footer = this.list.createChild({cls:cls+'-ft'});
16022             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16023                     {pageSize: this.pageSize});
16024             
16025         }
16026         
16027         if (this.pageTb && this.allowBlank && !this.disableClear) {
16028             var _this = this;
16029             this.pageTb.add(new Roo.Toolbar.Fill(), {
16030                 cls: 'x-btn-icon x-btn-clear',
16031                 text: '&#160;',
16032                 handler: function()
16033                 {
16034                     _this.collapse();
16035                     _this.clearValue();
16036                     _this.onSelect(false, -1);
16037                 }
16038             });
16039         }
16040         if (this.footer) {
16041             this.assetHeight += this.footer.getHeight();
16042         }
16043         */
16044             
16045         if(!this.tpl){
16046             this.tpl = Roo.bootstrap.version == 4 ?
16047                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16048                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16049         }
16050
16051         this.view = new Roo.View(this.list, this.tpl, {
16052             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16053         });
16054         //this.view.wrapEl.setDisplayed(false);
16055         this.view.on('click', this.onViewClick, this);
16056         
16057         
16058         this.store.on('beforeload', this.onBeforeLoad, this);
16059         this.store.on('load', this.onLoad, this);
16060         this.store.on('loadexception', this.onLoadException, this);
16061         /*
16062         if(this.resizable){
16063             this.resizer = new Roo.Resizable(this.list,  {
16064                pinned:true, handles:'se'
16065             });
16066             this.resizer.on('resize', function(r, w, h){
16067                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16068                 this.listWidth = w;
16069                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16070                 this.restrictHeight();
16071             }, this);
16072             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16073         }
16074         */
16075         if(!this.editable){
16076             this.editable = true;
16077             this.setEditable(false);
16078         }
16079         
16080         /*
16081         
16082         if (typeof(this.events.add.listeners) != 'undefined') {
16083             
16084             this.addicon = this.wrap.createChild(
16085                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16086        
16087             this.addicon.on('click', function(e) {
16088                 this.fireEvent('add', this);
16089             }, this);
16090         }
16091         if (typeof(this.events.edit.listeners) != 'undefined') {
16092             
16093             this.editicon = this.wrap.createChild(
16094                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16095             if (this.addicon) {
16096                 this.editicon.setStyle('margin-left', '40px');
16097             }
16098             this.editicon.on('click', function(e) {
16099                 
16100                 // we fire even  if inothing is selected..
16101                 this.fireEvent('edit', this, this.lastData );
16102                 
16103             }, this);
16104         }
16105         */
16106         
16107         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16108             "up" : function(e){
16109                 this.inKeyMode = true;
16110                 this.selectPrev();
16111             },
16112
16113             "down" : function(e){
16114                 if(!this.isExpanded()){
16115                     this.onTriggerClick();
16116                 }else{
16117                     this.inKeyMode = true;
16118                     this.selectNext();
16119                 }
16120             },
16121
16122             "enter" : function(e){
16123 //                this.onViewClick();
16124                 //return true;
16125                 this.collapse();
16126                 
16127                 if(this.fireEvent("specialkey", this, e)){
16128                     this.onViewClick(false);
16129                 }
16130                 
16131                 return true;
16132             },
16133
16134             "esc" : function(e){
16135                 this.collapse();
16136             },
16137
16138             "tab" : function(e){
16139                 this.collapse();
16140                 
16141                 if(this.fireEvent("specialkey", this, e)){
16142                     this.onViewClick(false);
16143                 }
16144                 
16145                 return true;
16146             },
16147
16148             scope : this,
16149
16150             doRelay : function(foo, bar, hname){
16151                 if(hname == 'down' || this.scope.isExpanded()){
16152                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16153                 }
16154                 return true;
16155             },
16156
16157             forceKeyDown: true
16158         });
16159         
16160         
16161         this.queryDelay = Math.max(this.queryDelay || 10,
16162                 this.mode == 'local' ? 10 : 250);
16163         
16164         
16165         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16166         
16167         if(this.typeAhead){
16168             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16169         }
16170         if(this.editable !== false){
16171             this.inputEl().on("keyup", this.onKeyUp, this);
16172         }
16173         if(this.forceSelection){
16174             this.inputEl().on('blur', this.doForce, this);
16175         }
16176         
16177         if(this.multiple){
16178             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16179             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16180         }
16181     },
16182     
16183     initTickableEvents: function()
16184     {   
16185         this.createList();
16186         
16187         if(this.hiddenName){
16188             
16189             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16190             
16191             this.hiddenField.dom.value =
16192                 this.hiddenValue !== undefined ? this.hiddenValue :
16193                 this.value !== undefined ? this.value : '';
16194
16195             // prevent input submission
16196             this.el.dom.removeAttribute('name');
16197             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16198              
16199              
16200         }
16201         
16202 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16203         
16204         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16205         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16206         if(this.triggerList){
16207             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16208         }
16209          
16210         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16211         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16212         
16213         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16214         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16215         
16216         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16217         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16218         
16219         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16220         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16221         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16222         
16223         this.okBtn.hide();
16224         this.cancelBtn.hide();
16225         
16226         var _this = this;
16227         
16228         (function(){
16229             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16230             _this.list.setWidth(lw);
16231         }).defer(100);
16232         
16233         this.list.on('mouseover', this.onViewOver, this);
16234         this.list.on('mousemove', this.onViewMove, this);
16235         
16236         this.list.on('scroll', this.onViewScroll, this);
16237         
16238         if(!this.tpl){
16239             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16240                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16241         }
16242
16243         this.view = new Roo.View(this.list, this.tpl, {
16244             singleSelect:true,
16245             tickable:true,
16246             parent:this,
16247             store: this.store,
16248             selectedClass: this.selectedClass
16249         });
16250         
16251         //this.view.wrapEl.setDisplayed(false);
16252         this.view.on('click', this.onViewClick, this);
16253         
16254         
16255         
16256         this.store.on('beforeload', this.onBeforeLoad, this);
16257         this.store.on('load', this.onLoad, this);
16258         this.store.on('loadexception', this.onLoadException, this);
16259         
16260         if(this.editable){
16261             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16262                 "up" : function(e){
16263                     this.inKeyMode = true;
16264                     this.selectPrev();
16265                 },
16266
16267                 "down" : function(e){
16268                     this.inKeyMode = true;
16269                     this.selectNext();
16270                 },
16271
16272                 "enter" : function(e){
16273                     if(this.fireEvent("specialkey", this, e)){
16274                         this.onViewClick(false);
16275                     }
16276                     
16277                     return true;
16278                 },
16279
16280                 "esc" : function(e){
16281                     this.onTickableFooterButtonClick(e, false, false);
16282                 },
16283
16284                 "tab" : function(e){
16285                     this.fireEvent("specialkey", this, e);
16286                     
16287                     this.onTickableFooterButtonClick(e, false, false);
16288                     
16289                     return true;
16290                 },
16291
16292                 scope : this,
16293
16294                 doRelay : function(e, fn, key){
16295                     if(this.scope.isExpanded()){
16296                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16297                     }
16298                     return true;
16299                 },
16300
16301                 forceKeyDown: true
16302             });
16303         }
16304         
16305         this.queryDelay = Math.max(this.queryDelay || 10,
16306                 this.mode == 'local' ? 10 : 250);
16307         
16308         
16309         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16310         
16311         if(this.typeAhead){
16312             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16313         }
16314         
16315         if(this.editable !== false){
16316             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16317         }
16318         
16319         this.indicator = this.indicatorEl();
16320         
16321         if(this.indicator){
16322             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16323             this.indicator.hide();
16324         }
16325         
16326     },
16327
16328     onDestroy : function(){
16329         if(this.view){
16330             this.view.setStore(null);
16331             this.view.el.removeAllListeners();
16332             this.view.el.remove();
16333             this.view.purgeListeners();
16334         }
16335         if(this.list){
16336             this.list.dom.innerHTML  = '';
16337         }
16338         
16339         if(this.store){
16340             this.store.un('beforeload', this.onBeforeLoad, this);
16341             this.store.un('load', this.onLoad, this);
16342             this.store.un('loadexception', this.onLoadException, this);
16343         }
16344         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16345     },
16346
16347     // private
16348     fireKey : function(e){
16349         if(e.isNavKeyPress() && !this.list.isVisible()){
16350             this.fireEvent("specialkey", this, e);
16351         }
16352     },
16353
16354     // private
16355     onResize: function(w, h)
16356     {
16357         
16358         
16359 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16360 //        
16361 //        if(typeof w != 'number'){
16362 //            // we do not handle it!?!?
16363 //            return;
16364 //        }
16365 //        var tw = this.trigger.getWidth();
16366 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16367 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16368 //        var x = w - tw;
16369 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16370 //            
16371 //        //this.trigger.setStyle('left', x+'px');
16372 //        
16373 //        if(this.list && this.listWidth === undefined){
16374 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16375 //            this.list.setWidth(lw);
16376 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16377 //        }
16378         
16379     
16380         
16381     },
16382
16383     /**
16384      * Allow or prevent the user from directly editing the field text.  If false is passed,
16385      * the user will only be able to select from the items defined in the dropdown list.  This method
16386      * is the runtime equivalent of setting the 'editable' config option at config time.
16387      * @param {Boolean} value True to allow the user to directly edit the field text
16388      */
16389     setEditable : function(value){
16390         if(value == this.editable){
16391             return;
16392         }
16393         this.editable = value;
16394         if(!value){
16395             this.inputEl().dom.setAttribute('readOnly', true);
16396             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16397             this.inputEl().addClass('x-combo-noedit');
16398         }else{
16399             this.inputEl().dom.removeAttribute('readOnly');
16400             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16401             this.inputEl().removeClass('x-combo-noedit');
16402         }
16403     },
16404
16405     // private
16406     
16407     onBeforeLoad : function(combo,opts){
16408         if(!this.hasFocus){
16409             return;
16410         }
16411          if (!opts.add) {
16412             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16413          }
16414         this.restrictHeight();
16415         this.selectedIndex = -1;
16416     },
16417
16418     // private
16419     onLoad : function(){
16420         
16421         this.hasQuery = false;
16422         
16423         if(!this.hasFocus){
16424             return;
16425         }
16426         
16427         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16428             this.loading.hide();
16429         }
16430         
16431         if(this.store.getCount() > 0){
16432             
16433             this.expand();
16434             this.restrictHeight();
16435             if(this.lastQuery == this.allQuery){
16436                 if(this.editable && !this.tickable){
16437                     this.inputEl().dom.select();
16438                 }
16439                 
16440                 if(
16441                     !this.selectByValue(this.value, true) &&
16442                     this.autoFocus && 
16443                     (
16444                         !this.store.lastOptions ||
16445                         typeof(this.store.lastOptions.add) == 'undefined' || 
16446                         this.store.lastOptions.add != true
16447                     )
16448                 ){
16449                     this.select(0, true);
16450                 }
16451             }else{
16452                 if(this.autoFocus){
16453                     this.selectNext();
16454                 }
16455                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16456                     this.taTask.delay(this.typeAheadDelay);
16457                 }
16458             }
16459         }else{
16460             this.onEmptyResults();
16461         }
16462         
16463         //this.el.focus();
16464     },
16465     // private
16466     onLoadException : function()
16467     {
16468         this.hasQuery = false;
16469         
16470         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16471             this.loading.hide();
16472         }
16473         
16474         if(this.tickable && this.editable){
16475             return;
16476         }
16477         
16478         this.collapse();
16479         // only causes errors at present
16480         //Roo.log(this.store.reader.jsonData);
16481         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16482             // fixme
16483             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16484         //}
16485         
16486         
16487     },
16488     // private
16489     onTypeAhead : function(){
16490         if(this.store.getCount() > 0){
16491             var r = this.store.getAt(0);
16492             var newValue = r.data[this.displayField];
16493             var len = newValue.length;
16494             var selStart = this.getRawValue().length;
16495             
16496             if(selStart != len){
16497                 this.setRawValue(newValue);
16498                 this.selectText(selStart, newValue.length);
16499             }
16500         }
16501     },
16502
16503     // private
16504     onSelect : function(record, index){
16505         
16506         if(this.fireEvent('beforeselect', this, record, index) !== false){
16507         
16508             this.setFromData(index > -1 ? record.data : false);
16509             
16510             this.collapse();
16511             this.fireEvent('select', this, record, index);
16512         }
16513     },
16514
16515     /**
16516      * Returns the currently selected field value or empty string if no value is set.
16517      * @return {String} value The selected value
16518      */
16519     getValue : function()
16520     {
16521         if(Roo.isIOS && this.useNativeIOS){
16522             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16523         }
16524         
16525         if(this.multiple){
16526             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16527         }
16528         
16529         if(this.valueField){
16530             return typeof this.value != 'undefined' ? this.value : '';
16531         }else{
16532             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16533         }
16534     },
16535     
16536     getRawValue : function()
16537     {
16538         if(Roo.isIOS && this.useNativeIOS){
16539             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16540         }
16541         
16542         var v = this.inputEl().getValue();
16543         
16544         return v;
16545     },
16546
16547     /**
16548      * Clears any text/value currently set in the field
16549      */
16550     clearValue : function(){
16551         
16552         if(this.hiddenField){
16553             this.hiddenField.dom.value = '';
16554         }
16555         this.value = '';
16556         this.setRawValue('');
16557         this.lastSelectionText = '';
16558         this.lastData = false;
16559         
16560         var close = this.closeTriggerEl();
16561         
16562         if(close){
16563             close.hide();
16564         }
16565         
16566         this.validate();
16567         
16568     },
16569
16570     /**
16571      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16572      * will be displayed in the field.  If the value does not match the data value of an existing item,
16573      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16574      * Otherwise the field will be blank (although the value will still be set).
16575      * @param {String} value The value to match
16576      */
16577     setValue : function(v)
16578     {
16579         if(Roo.isIOS && this.useNativeIOS){
16580             this.setIOSValue(v);
16581             return;
16582         }
16583         
16584         if(this.multiple){
16585             this.syncValue();
16586             return;
16587         }
16588         
16589         var text = v;
16590         if(this.valueField){
16591             var r = this.findRecord(this.valueField, v);
16592             if(r){
16593                 text = r.data[this.displayField];
16594             }else if(this.valueNotFoundText !== undefined){
16595                 text = this.valueNotFoundText;
16596             }
16597         }
16598         this.lastSelectionText = text;
16599         if(this.hiddenField){
16600             this.hiddenField.dom.value = v;
16601         }
16602         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16603         this.value = v;
16604         
16605         var close = this.closeTriggerEl();
16606         
16607         if(close){
16608             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16609         }
16610         
16611         this.validate();
16612     },
16613     /**
16614      * @property {Object} the last set data for the element
16615      */
16616     
16617     lastData : false,
16618     /**
16619      * Sets the value of the field based on a object which is related to the record format for the store.
16620      * @param {Object} value the value to set as. or false on reset?
16621      */
16622     setFromData : function(o){
16623         
16624         if(this.multiple){
16625             this.addItem(o);
16626             return;
16627         }
16628             
16629         var dv = ''; // display value
16630         var vv = ''; // value value..
16631         this.lastData = o;
16632         if (this.displayField) {
16633             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16634         } else {
16635             // this is an error condition!!!
16636             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16637         }
16638         
16639         if(this.valueField){
16640             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16641         }
16642         
16643         var close = this.closeTriggerEl();
16644         
16645         if(close){
16646             if(dv.length || vv * 1 > 0){
16647                 close.show() ;
16648                 this.blockFocus=true;
16649             } else {
16650                 close.hide();
16651             }             
16652         }
16653         
16654         if(this.hiddenField){
16655             this.hiddenField.dom.value = vv;
16656             
16657             this.lastSelectionText = dv;
16658             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16659             this.value = vv;
16660             return;
16661         }
16662         // no hidden field.. - we store the value in 'value', but still display
16663         // display field!!!!
16664         this.lastSelectionText = dv;
16665         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16666         this.value = vv;
16667         
16668         
16669         
16670     },
16671     // private
16672     reset : function(){
16673         // overridden so that last data is reset..
16674         
16675         if(this.multiple){
16676             this.clearItem();
16677             return;
16678         }
16679         
16680         this.setValue(this.originalValue);
16681         //this.clearInvalid();
16682         this.lastData = false;
16683         if (this.view) {
16684             this.view.clearSelections();
16685         }
16686         
16687         this.validate();
16688     },
16689     // private
16690     findRecord : function(prop, value){
16691         var record;
16692         if(this.store.getCount() > 0){
16693             this.store.each(function(r){
16694                 if(r.data[prop] == value){
16695                     record = r;
16696                     return false;
16697                 }
16698                 return true;
16699             });
16700         }
16701         return record;
16702     },
16703     
16704     getName: function()
16705     {
16706         // returns hidden if it's set..
16707         if (!this.rendered) {return ''};
16708         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16709         
16710     },
16711     // private
16712     onViewMove : function(e, t){
16713         this.inKeyMode = false;
16714     },
16715
16716     // private
16717     onViewOver : function(e, t){
16718         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16719             return;
16720         }
16721         var item = this.view.findItemFromChild(t);
16722         
16723         if(item){
16724             var index = this.view.indexOf(item);
16725             this.select(index, false);
16726         }
16727     },
16728
16729     // private
16730     onViewClick : function(view, doFocus, el, e)
16731     {
16732         var index = this.view.getSelectedIndexes()[0];
16733         
16734         var r = this.store.getAt(index);
16735         
16736         if(this.tickable){
16737             
16738             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16739                 return;
16740             }
16741             
16742             var rm = false;
16743             var _this = this;
16744             
16745             Roo.each(this.tickItems, function(v,k){
16746                 
16747                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16748                     Roo.log(v);
16749                     _this.tickItems.splice(k, 1);
16750                     
16751                     if(typeof(e) == 'undefined' && view == false){
16752                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16753                     }
16754                     
16755                     rm = true;
16756                     return;
16757                 }
16758             });
16759             
16760             if(rm){
16761                 return;
16762             }
16763             
16764             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16765                 this.tickItems.push(r.data);
16766             }
16767             
16768             if(typeof(e) == 'undefined' && view == false){
16769                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16770             }
16771                     
16772             return;
16773         }
16774         
16775         if(r){
16776             this.onSelect(r, index);
16777         }
16778         if(doFocus !== false && !this.blockFocus){
16779             this.inputEl().focus();
16780         }
16781     },
16782
16783     // private
16784     restrictHeight : function(){
16785         //this.innerList.dom.style.height = '';
16786         //var inner = this.innerList.dom;
16787         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16788         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16789         //this.list.beginUpdate();
16790         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16791         this.list.alignTo(this.inputEl(), this.listAlign);
16792         this.list.alignTo(this.inputEl(), this.listAlign);
16793         //this.list.endUpdate();
16794     },
16795
16796     // private
16797     onEmptyResults : function(){
16798         
16799         if(this.tickable && this.editable){
16800             this.hasFocus = false;
16801             this.restrictHeight();
16802             return;
16803         }
16804         
16805         this.collapse();
16806     },
16807
16808     /**
16809      * Returns true if the dropdown list is expanded, else false.
16810      */
16811     isExpanded : function(){
16812         return this.list.isVisible();
16813     },
16814
16815     /**
16816      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16817      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16818      * @param {String} value The data value of the item to select
16819      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16820      * selected item if it is not currently in view (defaults to true)
16821      * @return {Boolean} True if the value matched an item in the list, else false
16822      */
16823     selectByValue : function(v, scrollIntoView){
16824         if(v !== undefined && v !== null){
16825             var r = this.findRecord(this.valueField || this.displayField, v);
16826             if(r){
16827                 this.select(this.store.indexOf(r), scrollIntoView);
16828                 return true;
16829             }
16830         }
16831         return false;
16832     },
16833
16834     /**
16835      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16836      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16837      * @param {Number} index The zero-based index of the list item to select
16838      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16839      * selected item if it is not currently in view (defaults to true)
16840      */
16841     select : function(index, scrollIntoView){
16842         this.selectedIndex = index;
16843         this.view.select(index);
16844         if(scrollIntoView !== false){
16845             var el = this.view.getNode(index);
16846             /*
16847              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16848              */
16849             if(el){
16850                 this.list.scrollChildIntoView(el, false);
16851             }
16852         }
16853     },
16854
16855     // private
16856     selectNext : function(){
16857         var ct = this.store.getCount();
16858         if(ct > 0){
16859             if(this.selectedIndex == -1){
16860                 this.select(0);
16861             }else if(this.selectedIndex < ct-1){
16862                 this.select(this.selectedIndex+1);
16863             }
16864         }
16865     },
16866
16867     // private
16868     selectPrev : function(){
16869         var ct = this.store.getCount();
16870         if(ct > 0){
16871             if(this.selectedIndex == -1){
16872                 this.select(0);
16873             }else if(this.selectedIndex != 0){
16874                 this.select(this.selectedIndex-1);
16875             }
16876         }
16877     },
16878
16879     // private
16880     onKeyUp : function(e){
16881         if(this.editable !== false && !e.isSpecialKey()){
16882             this.lastKey = e.getKey();
16883             this.dqTask.delay(this.queryDelay);
16884         }
16885     },
16886
16887     // private
16888     validateBlur : function(){
16889         return !this.list || !this.list.isVisible();   
16890     },
16891
16892     // private
16893     initQuery : function(){
16894         
16895         var v = this.getRawValue();
16896         
16897         if(this.tickable && this.editable){
16898             v = this.tickableInputEl().getValue();
16899         }
16900         
16901         this.doQuery(v);
16902     },
16903
16904     // private
16905     doForce : function(){
16906         if(this.inputEl().dom.value.length > 0){
16907             this.inputEl().dom.value =
16908                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16909              
16910         }
16911     },
16912
16913     /**
16914      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16915      * query allowing the query action to be canceled if needed.
16916      * @param {String} query The SQL query to execute
16917      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16918      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16919      * saved in the current store (defaults to false)
16920      */
16921     doQuery : function(q, forceAll){
16922         
16923         if(q === undefined || q === null){
16924             q = '';
16925         }
16926         var qe = {
16927             query: q,
16928             forceAll: forceAll,
16929             combo: this,
16930             cancel:false
16931         };
16932         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16933             return false;
16934         }
16935         q = qe.query;
16936         
16937         forceAll = qe.forceAll;
16938         if(forceAll === true || (q.length >= this.minChars)){
16939             
16940             this.hasQuery = true;
16941             
16942             if(this.lastQuery != q || this.alwaysQuery){
16943                 this.lastQuery = q;
16944                 if(this.mode == 'local'){
16945                     this.selectedIndex = -1;
16946                     if(forceAll){
16947                         this.store.clearFilter();
16948                     }else{
16949                         
16950                         if(this.specialFilter){
16951                             this.fireEvent('specialfilter', this);
16952                             this.onLoad();
16953                             return;
16954                         }
16955                         
16956                         this.store.filter(this.displayField, q);
16957                     }
16958                     
16959                     this.store.fireEvent("datachanged", this.store);
16960                     
16961                     this.onLoad();
16962                     
16963                     
16964                 }else{
16965                     
16966                     this.store.baseParams[this.queryParam] = q;
16967                     
16968                     var options = {params : this.getParams(q)};
16969                     
16970                     if(this.loadNext){
16971                         options.add = true;
16972                         options.params.start = this.page * this.pageSize;
16973                     }
16974                     
16975                     this.store.load(options);
16976                     
16977                     /*
16978                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16979                      *  we should expand the list on onLoad
16980                      *  so command out it
16981                      */
16982 //                    this.expand();
16983                 }
16984             }else{
16985                 this.selectedIndex = -1;
16986                 this.onLoad();   
16987             }
16988         }
16989         
16990         this.loadNext = false;
16991     },
16992     
16993     // private
16994     getParams : function(q){
16995         var p = {};
16996         //p[this.queryParam] = q;
16997         
16998         if(this.pageSize){
16999             p.start = 0;
17000             p.limit = this.pageSize;
17001         }
17002         return p;
17003     },
17004
17005     /**
17006      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17007      */
17008     collapse : function(){
17009         if(!this.isExpanded()){
17010             return;
17011         }
17012         
17013         this.list.hide();
17014         
17015         this.hasFocus = false;
17016         
17017         if(this.tickable){
17018             this.okBtn.hide();
17019             this.cancelBtn.hide();
17020             this.trigger.show();
17021             
17022             if(this.editable){
17023                 this.tickableInputEl().dom.value = '';
17024                 this.tickableInputEl().blur();
17025             }
17026             
17027         }
17028         
17029         Roo.get(document).un('mousedown', this.collapseIf, this);
17030         Roo.get(document).un('mousewheel', this.collapseIf, this);
17031         if (!this.editable) {
17032             Roo.get(document).un('keydown', this.listKeyPress, this);
17033         }
17034         this.fireEvent('collapse', this);
17035         
17036         this.validate();
17037     },
17038
17039     // private
17040     collapseIf : function(e){
17041         var in_combo  = e.within(this.el);
17042         var in_list =  e.within(this.list);
17043         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17044         
17045         if (in_combo || in_list || is_list) {
17046             //e.stopPropagation();
17047             return;
17048         }
17049         
17050         if(this.tickable){
17051             this.onTickableFooterButtonClick(e, false, false);
17052         }
17053
17054         this.collapse();
17055         
17056     },
17057
17058     /**
17059      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17060      */
17061     expand : function(){
17062        
17063         if(this.isExpanded() || !this.hasFocus){
17064             return;
17065         }
17066         
17067         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17068         this.list.setWidth(lw);
17069         
17070         Roo.log('expand');
17071         
17072         this.list.show();
17073         
17074         this.restrictHeight();
17075         
17076         if(this.tickable){
17077             
17078             this.tickItems = Roo.apply([], this.item);
17079             
17080             this.okBtn.show();
17081             this.cancelBtn.show();
17082             this.trigger.hide();
17083             
17084             if(this.editable){
17085                 this.tickableInputEl().focus();
17086             }
17087             
17088         }
17089         
17090         Roo.get(document).on('mousedown', this.collapseIf, this);
17091         Roo.get(document).on('mousewheel', this.collapseIf, this);
17092         if (!this.editable) {
17093             Roo.get(document).on('keydown', this.listKeyPress, this);
17094         }
17095         
17096         this.fireEvent('expand', this);
17097     },
17098
17099     // private
17100     // Implements the default empty TriggerField.onTriggerClick function
17101     onTriggerClick : function(e)
17102     {
17103         Roo.log('trigger click');
17104         
17105         if(this.disabled || !this.triggerList){
17106             return;
17107         }
17108         
17109         this.page = 0;
17110         this.loadNext = false;
17111         
17112         if(this.isExpanded()){
17113             this.collapse();
17114             if (!this.blockFocus) {
17115                 this.inputEl().focus();
17116             }
17117             
17118         }else {
17119             this.hasFocus = true;
17120             if(this.triggerAction == 'all') {
17121                 this.doQuery(this.allQuery, true);
17122             } else {
17123                 this.doQuery(this.getRawValue());
17124             }
17125             if (!this.blockFocus) {
17126                 this.inputEl().focus();
17127             }
17128         }
17129     },
17130     
17131     onTickableTriggerClick : function(e)
17132     {
17133         if(this.disabled){
17134             return;
17135         }
17136         
17137         this.page = 0;
17138         this.loadNext = false;
17139         this.hasFocus = true;
17140         
17141         if(this.triggerAction == 'all') {
17142             this.doQuery(this.allQuery, true);
17143         } else {
17144             this.doQuery(this.getRawValue());
17145         }
17146     },
17147     
17148     onSearchFieldClick : function(e)
17149     {
17150         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17151             this.onTickableFooterButtonClick(e, false, false);
17152             return;
17153         }
17154         
17155         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17156             return;
17157         }
17158         
17159         this.page = 0;
17160         this.loadNext = false;
17161         this.hasFocus = true;
17162         
17163         if(this.triggerAction == 'all') {
17164             this.doQuery(this.allQuery, true);
17165         } else {
17166             this.doQuery(this.getRawValue());
17167         }
17168     },
17169     
17170     listKeyPress : function(e)
17171     {
17172         //Roo.log('listkeypress');
17173         // scroll to first matching element based on key pres..
17174         if (e.isSpecialKey()) {
17175             return false;
17176         }
17177         var k = String.fromCharCode(e.getKey()).toUpperCase();
17178         //Roo.log(k);
17179         var match  = false;
17180         var csel = this.view.getSelectedNodes();
17181         var cselitem = false;
17182         if (csel.length) {
17183             var ix = this.view.indexOf(csel[0]);
17184             cselitem  = this.store.getAt(ix);
17185             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17186                 cselitem = false;
17187             }
17188             
17189         }
17190         
17191         this.store.each(function(v) { 
17192             if (cselitem) {
17193                 // start at existing selection.
17194                 if (cselitem.id == v.id) {
17195                     cselitem = false;
17196                 }
17197                 return true;
17198             }
17199                 
17200             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17201                 match = this.store.indexOf(v);
17202                 return false;
17203             }
17204             return true;
17205         }, this);
17206         
17207         if (match === false) {
17208             return true; // no more action?
17209         }
17210         // scroll to?
17211         this.view.select(match);
17212         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17213         sn.scrollIntoView(sn.dom.parentNode, false);
17214     },
17215     
17216     onViewScroll : function(e, t){
17217         
17218         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){
17219             return;
17220         }
17221         
17222         this.hasQuery = true;
17223         
17224         this.loading = this.list.select('.loading', true).first();
17225         
17226         if(this.loading === null){
17227             this.list.createChild({
17228                 tag: 'div',
17229                 cls: 'loading roo-select2-more-results roo-select2-active',
17230                 html: 'Loading more results...'
17231             });
17232             
17233             this.loading = this.list.select('.loading', true).first();
17234             
17235             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17236             
17237             this.loading.hide();
17238         }
17239         
17240         this.loading.show();
17241         
17242         var _combo = this;
17243         
17244         this.page++;
17245         this.loadNext = true;
17246         
17247         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17248         
17249         return;
17250     },
17251     
17252     addItem : function(o)
17253     {   
17254         var dv = ''; // display value
17255         
17256         if (this.displayField) {
17257             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17258         } else {
17259             // this is an error condition!!!
17260             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17261         }
17262         
17263         if(!dv.length){
17264             return;
17265         }
17266         
17267         var choice = this.choices.createChild({
17268             tag: 'li',
17269             cls: 'roo-select2-search-choice',
17270             cn: [
17271                 {
17272                     tag: 'div',
17273                     html: dv
17274                 },
17275                 {
17276                     tag: 'a',
17277                     href: '#',
17278                     cls: 'roo-select2-search-choice-close fa fa-times',
17279                     tabindex: '-1'
17280                 }
17281             ]
17282             
17283         }, this.searchField);
17284         
17285         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17286         
17287         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17288         
17289         this.item.push(o);
17290         
17291         this.lastData = o;
17292         
17293         this.syncValue();
17294         
17295         this.inputEl().dom.value = '';
17296         
17297         this.validate();
17298     },
17299     
17300     onRemoveItem : function(e, _self, o)
17301     {
17302         e.preventDefault();
17303         
17304         this.lastItem = Roo.apply([], this.item);
17305         
17306         var index = this.item.indexOf(o.data) * 1;
17307         
17308         if( index < 0){
17309             Roo.log('not this item?!');
17310             return;
17311         }
17312         
17313         this.item.splice(index, 1);
17314         o.item.remove();
17315         
17316         this.syncValue();
17317         
17318         this.fireEvent('remove', this, e);
17319         
17320         this.validate();
17321         
17322     },
17323     
17324     syncValue : function()
17325     {
17326         if(!this.item.length){
17327             this.clearValue();
17328             return;
17329         }
17330             
17331         var value = [];
17332         var _this = this;
17333         Roo.each(this.item, function(i){
17334             if(_this.valueField){
17335                 value.push(i[_this.valueField]);
17336                 return;
17337             }
17338
17339             value.push(i);
17340         });
17341
17342         this.value = value.join(',');
17343
17344         if(this.hiddenField){
17345             this.hiddenField.dom.value = this.value;
17346         }
17347         
17348         this.store.fireEvent("datachanged", this.store);
17349         
17350         this.validate();
17351     },
17352     
17353     clearItem : function()
17354     {
17355         if(!this.multiple){
17356             return;
17357         }
17358         
17359         this.item = [];
17360         
17361         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17362            c.remove();
17363         });
17364         
17365         this.syncValue();
17366         
17367         this.validate();
17368         
17369         if(this.tickable && !Roo.isTouch){
17370             this.view.refresh();
17371         }
17372     },
17373     
17374     inputEl: function ()
17375     {
17376         if(Roo.isIOS && this.useNativeIOS){
17377             return this.el.select('select.roo-ios-select', true).first();
17378         }
17379         
17380         if(Roo.isTouch && this.mobileTouchView){
17381             return this.el.select('input.form-control',true).first();
17382         }
17383         
17384         if(this.tickable){
17385             return this.searchField;
17386         }
17387         
17388         return this.el.select('input.form-control',true).first();
17389     },
17390     
17391     onTickableFooterButtonClick : function(e, btn, el)
17392     {
17393         e.preventDefault();
17394         
17395         this.lastItem = Roo.apply([], this.item);
17396         
17397         if(btn && btn.name == 'cancel'){
17398             this.tickItems = Roo.apply([], this.item);
17399             this.collapse();
17400             return;
17401         }
17402         
17403         this.clearItem();
17404         
17405         var _this = this;
17406         
17407         Roo.each(this.tickItems, function(o){
17408             _this.addItem(o);
17409         });
17410         
17411         this.collapse();
17412         
17413     },
17414     
17415     validate : function()
17416     {
17417         if(this.getVisibilityEl().hasClass('hidden')){
17418             return true;
17419         }
17420         
17421         var v = this.getRawValue();
17422         
17423         if(this.multiple){
17424             v = this.getValue();
17425         }
17426         
17427         if(this.disabled || this.allowBlank || v.length){
17428             this.markValid();
17429             return true;
17430         }
17431         
17432         this.markInvalid();
17433         return false;
17434     },
17435     
17436     tickableInputEl : function()
17437     {
17438         if(!this.tickable || !this.editable){
17439             return this.inputEl();
17440         }
17441         
17442         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17443     },
17444     
17445     
17446     getAutoCreateTouchView : function()
17447     {
17448         var id = Roo.id();
17449         
17450         var cfg = {
17451             cls: 'form-group' //input-group
17452         };
17453         
17454         var input =  {
17455             tag: 'input',
17456             id : id,
17457             type : this.inputType,
17458             cls : 'form-control x-combo-noedit',
17459             autocomplete: 'new-password',
17460             placeholder : this.placeholder || '',
17461             readonly : true
17462         };
17463         
17464         if (this.name) {
17465             input.name = this.name;
17466         }
17467         
17468         if (this.size) {
17469             input.cls += ' input-' + this.size;
17470         }
17471         
17472         if (this.disabled) {
17473             input.disabled = true;
17474         }
17475         
17476         var inputblock = {
17477             cls : 'roo-combobox-wrap',
17478             cn : [
17479                 input
17480             ]
17481         };
17482         
17483         if(this.before){
17484             inputblock.cls += ' input-group';
17485             
17486             inputblock.cn.unshift({
17487                 tag :'span',
17488                 cls : 'input-group-addon input-group-prepend input-group-text',
17489                 html : this.before
17490             });
17491         }
17492         
17493         if(this.removable && !this.multiple){
17494             inputblock.cls += ' roo-removable';
17495             
17496             inputblock.cn.push({
17497                 tag: 'button',
17498                 html : 'x',
17499                 cls : 'roo-combo-removable-btn close'
17500             });
17501         }
17502
17503         if(this.hasFeedback && !this.allowBlank){
17504             
17505             inputblock.cls += ' has-feedback';
17506             
17507             inputblock.cn.push({
17508                 tag: 'span',
17509                 cls: 'glyphicon form-control-feedback'
17510             });
17511             
17512         }
17513         
17514         if (this.after) {
17515             
17516             inputblock.cls += (this.before) ? '' : ' input-group';
17517             
17518             inputblock.cn.push({
17519                 tag :'span',
17520                 cls : 'input-group-addon input-group-append input-group-text',
17521                 html : this.after
17522             });
17523         }
17524
17525         
17526         var ibwrap = inputblock;
17527         
17528         if(this.multiple){
17529             ibwrap = {
17530                 tag: 'ul',
17531                 cls: 'roo-select2-choices',
17532                 cn:[
17533                     {
17534                         tag: 'li',
17535                         cls: 'roo-select2-search-field',
17536                         cn: [
17537
17538                             inputblock
17539                         ]
17540                     }
17541                 ]
17542             };
17543         
17544             
17545         }
17546         
17547         var combobox = {
17548             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17549             cn: [
17550                 {
17551                     tag: 'input',
17552                     type : 'hidden',
17553                     cls: 'form-hidden-field'
17554                 },
17555                 ibwrap
17556             ]
17557         };
17558         
17559         if(!this.multiple && this.showToggleBtn){
17560             
17561             var caret = {
17562                 cls: 'caret'
17563             };
17564             
17565             if (this.caret != false) {
17566                 caret = {
17567                      tag: 'i',
17568                      cls: 'fa fa-' + this.caret
17569                 };
17570                 
17571             }
17572             
17573             combobox.cn.push({
17574                 tag :'span',
17575                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17576                 cn : [
17577                     Roo.bootstrap.version == 3 ? caret : '',
17578                     {
17579                         tag: 'span',
17580                         cls: 'combobox-clear',
17581                         cn  : [
17582                             {
17583                                 tag : 'i',
17584                                 cls: 'icon-remove'
17585                             }
17586                         ]
17587                     }
17588                 ]
17589
17590             })
17591         }
17592         
17593         if(this.multiple){
17594             combobox.cls += ' roo-select2-container-multi';
17595         }
17596         
17597         var required =  this.allowBlank ?  {
17598                     tag : 'i',
17599                     style: 'display: none'
17600                 } : {
17601                    tag : 'i',
17602                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17603                    tooltip : 'This field is required'
17604                 };
17605         
17606         var align = this.labelAlign || this.parentLabelAlign();
17607         
17608         if (align ==='left' && this.fieldLabel.length) {
17609
17610             cfg.cn = [
17611                 required,
17612                 {
17613                     tag: 'label',
17614                     cls : 'control-label col-form-label',
17615                     html : this.fieldLabel
17616
17617                 },
17618                 {
17619                     cls : 'roo-combobox-wrap ', 
17620                     cn: [
17621                         combobox
17622                     ]
17623                 }
17624             ];
17625             
17626             var labelCfg = cfg.cn[1];
17627             var contentCfg = cfg.cn[2];
17628             
17629
17630             if(this.indicatorpos == 'right'){
17631                 cfg.cn = [
17632                     {
17633                         tag: 'label',
17634                         'for' :  id,
17635                         cls : 'control-label col-form-label',
17636                         cn : [
17637                             {
17638                                 tag : 'span',
17639                                 html : this.fieldLabel
17640                             },
17641                             required
17642                         ]
17643                     },
17644                     {
17645                         cls : "roo-combobox-wrap ",
17646                         cn: [
17647                             combobox
17648                         ]
17649                     }
17650
17651                 ];
17652                 
17653                 labelCfg = cfg.cn[0];
17654                 contentCfg = cfg.cn[1];
17655             }
17656             
17657            
17658             
17659             if(this.labelWidth > 12){
17660                 labelCfg.style = "width: " + this.labelWidth + 'px';
17661             }
17662            
17663             if(this.labelWidth < 13 && this.labelmd == 0){
17664                 this.labelmd = this.labelWidth;
17665             }
17666             
17667             if(this.labellg > 0){
17668                 labelCfg.cls += ' col-lg-' + this.labellg;
17669                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17670             }
17671             
17672             if(this.labelmd > 0){
17673                 labelCfg.cls += ' col-md-' + this.labelmd;
17674                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17675             }
17676             
17677             if(this.labelsm > 0){
17678                 labelCfg.cls += ' col-sm-' + this.labelsm;
17679                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17680             }
17681             
17682             if(this.labelxs > 0){
17683                 labelCfg.cls += ' col-xs-' + this.labelxs;
17684                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17685             }
17686                 
17687                 
17688         } else if ( this.fieldLabel.length) {
17689             cfg.cn = [
17690                required,
17691                 {
17692                     tag: 'label',
17693                     cls : 'control-label',
17694                     html : this.fieldLabel
17695
17696                 },
17697                 {
17698                     cls : '', 
17699                     cn: [
17700                         combobox
17701                     ]
17702                 }
17703             ];
17704             
17705             if(this.indicatorpos == 'right'){
17706                 cfg.cn = [
17707                     {
17708                         tag: 'label',
17709                         cls : 'control-label',
17710                         html : this.fieldLabel,
17711                         cn : [
17712                             required
17713                         ]
17714                     },
17715                     {
17716                         cls : '', 
17717                         cn: [
17718                             combobox
17719                         ]
17720                     }
17721                 ];
17722             }
17723         } else {
17724             cfg.cn = combobox;    
17725         }
17726         
17727         
17728         var settings = this;
17729         
17730         ['xs','sm','md','lg'].map(function(size){
17731             if (settings[size]) {
17732                 cfg.cls += ' col-' + size + '-' + settings[size];
17733             }
17734         });
17735         
17736         return cfg;
17737     },
17738     
17739     initTouchView : function()
17740     {
17741         this.renderTouchView();
17742         
17743         this.touchViewEl.on('scroll', function(){
17744             this.el.dom.scrollTop = 0;
17745         }, this);
17746         
17747         this.originalValue = this.getValue();
17748         
17749         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17750         
17751         this.inputEl().on("click", this.showTouchView, this);
17752         if (this.triggerEl) {
17753             this.triggerEl.on("click", this.showTouchView, this);
17754         }
17755         
17756         
17757         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17758         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17759         
17760         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17761         
17762         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17763         this.store.on('load', this.onTouchViewLoad, this);
17764         this.store.on('loadexception', this.onTouchViewLoadException, this);
17765         
17766         if(this.hiddenName){
17767             
17768             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17769             
17770             this.hiddenField.dom.value =
17771                 this.hiddenValue !== undefined ? this.hiddenValue :
17772                 this.value !== undefined ? this.value : '';
17773         
17774             this.el.dom.removeAttribute('name');
17775             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17776         }
17777         
17778         if(this.multiple){
17779             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17780             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17781         }
17782         
17783         if(this.removable && !this.multiple){
17784             var close = this.closeTriggerEl();
17785             if(close){
17786                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17787                 close.on('click', this.removeBtnClick, this, close);
17788             }
17789         }
17790         /*
17791          * fix the bug in Safari iOS8
17792          */
17793         this.inputEl().on("focus", function(e){
17794             document.activeElement.blur();
17795         }, this);
17796         
17797         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17798         
17799         return;
17800         
17801         
17802     },
17803     
17804     renderTouchView : function()
17805     {
17806         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17807         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17808         
17809         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17810         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17811         
17812         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17813         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17814         this.touchViewBodyEl.setStyle('overflow', 'auto');
17815         
17816         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17817         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17818         
17819         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17820         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17821         
17822     },
17823     
17824     showTouchView : function()
17825     {
17826         if(this.disabled){
17827             return;
17828         }
17829         
17830         this.touchViewHeaderEl.hide();
17831
17832         if(this.modalTitle.length){
17833             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17834             this.touchViewHeaderEl.show();
17835         }
17836
17837         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17838         this.touchViewEl.show();
17839
17840         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17841         
17842         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17843         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17844
17845         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17846
17847         if(this.modalTitle.length){
17848             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17849         }
17850         
17851         this.touchViewBodyEl.setHeight(bodyHeight);
17852
17853         if(this.animate){
17854             var _this = this;
17855             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17856         }else{
17857             this.touchViewEl.addClass(['in','show']);
17858         }
17859         
17860         if(this._touchViewMask){
17861             Roo.get(document.body).addClass("x-body-masked");
17862             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17863             this._touchViewMask.setStyle('z-index', 10000);
17864             this._touchViewMask.addClass('show');
17865         }
17866         
17867         this.doTouchViewQuery();
17868         
17869     },
17870     
17871     hideTouchView : function()
17872     {
17873         this.touchViewEl.removeClass(['in','show']);
17874
17875         if(this.animate){
17876             var _this = this;
17877             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17878         }else{
17879             this.touchViewEl.setStyle('display', 'none');
17880         }
17881         
17882         if(this._touchViewMask){
17883             this._touchViewMask.removeClass('show');
17884             Roo.get(document.body).removeClass("x-body-masked");
17885         }
17886     },
17887     
17888     setTouchViewValue : function()
17889     {
17890         if(this.multiple){
17891             this.clearItem();
17892         
17893             var _this = this;
17894
17895             Roo.each(this.tickItems, function(o){
17896                 this.addItem(o);
17897             }, this);
17898         }
17899         
17900         this.hideTouchView();
17901     },
17902     
17903     doTouchViewQuery : function()
17904     {
17905         var qe = {
17906             query: '',
17907             forceAll: true,
17908             combo: this,
17909             cancel:false
17910         };
17911         
17912         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17913             return false;
17914         }
17915         
17916         if(!this.alwaysQuery || this.mode == 'local'){
17917             this.onTouchViewLoad();
17918             return;
17919         }
17920         
17921         this.store.load();
17922     },
17923     
17924     onTouchViewBeforeLoad : function(combo,opts)
17925     {
17926         return;
17927     },
17928
17929     // private
17930     onTouchViewLoad : function()
17931     {
17932         if(this.store.getCount() < 1){
17933             this.onTouchViewEmptyResults();
17934             return;
17935         }
17936         
17937         this.clearTouchView();
17938         
17939         var rawValue = this.getRawValue();
17940         
17941         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17942         
17943         this.tickItems = [];
17944         
17945         this.store.data.each(function(d, rowIndex){
17946             var row = this.touchViewListGroup.createChild(template);
17947             
17948             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17949                 row.addClass(d.data.cls);
17950             }
17951             
17952             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17953                 var cfg = {
17954                     data : d.data,
17955                     html : d.data[this.displayField]
17956                 };
17957                 
17958                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17959                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17960                 }
17961             }
17962             row.removeClass('selected');
17963             if(!this.multiple && this.valueField &&
17964                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17965             {
17966                 // radio buttons..
17967                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17968                 row.addClass('selected');
17969             }
17970             
17971             if(this.multiple && this.valueField &&
17972                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17973             {
17974                 
17975                 // checkboxes...
17976                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17977                 this.tickItems.push(d.data);
17978             }
17979             
17980             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17981             
17982         }, this);
17983         
17984         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17985         
17986         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17987
17988         if(this.modalTitle.length){
17989             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17990         }
17991
17992         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17993         
17994         if(this.mobile_restrict_height && listHeight < bodyHeight){
17995             this.touchViewBodyEl.setHeight(listHeight);
17996         }
17997         
17998         var _this = this;
17999         
18000         if(firstChecked && listHeight > bodyHeight){
18001             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18002         }
18003         
18004     },
18005     
18006     onTouchViewLoadException : function()
18007     {
18008         this.hideTouchView();
18009     },
18010     
18011     onTouchViewEmptyResults : function()
18012     {
18013         this.clearTouchView();
18014         
18015         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18016         
18017         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18018         
18019     },
18020     
18021     clearTouchView : function()
18022     {
18023         this.touchViewListGroup.dom.innerHTML = '';
18024     },
18025     
18026     onTouchViewClick : function(e, el, o)
18027     {
18028         e.preventDefault();
18029         
18030         var row = o.row;
18031         var rowIndex = o.rowIndex;
18032         
18033         var r = this.store.getAt(rowIndex);
18034         
18035         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18036             
18037             if(!this.multiple){
18038                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18039                     c.dom.removeAttribute('checked');
18040                 }, this);
18041
18042                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18043
18044                 this.setFromData(r.data);
18045
18046                 var close = this.closeTriggerEl();
18047
18048                 if(close){
18049                     close.show();
18050                 }
18051
18052                 this.hideTouchView();
18053
18054                 this.fireEvent('select', this, r, rowIndex);
18055
18056                 return;
18057             }
18058
18059             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18060                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18061                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18062                 return;
18063             }
18064
18065             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18066             this.addItem(r.data);
18067             this.tickItems.push(r.data);
18068         }
18069     },
18070     
18071     getAutoCreateNativeIOS : function()
18072     {
18073         var cfg = {
18074             cls: 'form-group' //input-group,
18075         };
18076         
18077         var combobox =  {
18078             tag: 'select',
18079             cls : 'roo-ios-select'
18080         };
18081         
18082         if (this.name) {
18083             combobox.name = this.name;
18084         }
18085         
18086         if (this.disabled) {
18087             combobox.disabled = true;
18088         }
18089         
18090         var settings = this;
18091         
18092         ['xs','sm','md','lg'].map(function(size){
18093             if (settings[size]) {
18094                 cfg.cls += ' col-' + size + '-' + settings[size];
18095             }
18096         });
18097         
18098         cfg.cn = combobox;
18099         
18100         return cfg;
18101         
18102     },
18103     
18104     initIOSView : function()
18105     {
18106         this.store.on('load', this.onIOSViewLoad, this);
18107         
18108         return;
18109     },
18110     
18111     onIOSViewLoad : function()
18112     {
18113         if(this.store.getCount() < 1){
18114             return;
18115         }
18116         
18117         this.clearIOSView();
18118         
18119         if(this.allowBlank) {
18120             
18121             var default_text = '-- SELECT --';
18122             
18123             if(this.placeholder.length){
18124                 default_text = this.placeholder;
18125             }
18126             
18127             if(this.emptyTitle.length){
18128                 default_text += ' - ' + this.emptyTitle + ' -';
18129             }
18130             
18131             var opt = this.inputEl().createChild({
18132                 tag: 'option',
18133                 value : 0,
18134                 html : default_text
18135             });
18136             
18137             var o = {};
18138             o[this.valueField] = 0;
18139             o[this.displayField] = default_text;
18140             
18141             this.ios_options.push({
18142                 data : o,
18143                 el : opt
18144             });
18145             
18146         }
18147         
18148         this.store.data.each(function(d, rowIndex){
18149             
18150             var html = '';
18151             
18152             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18153                 html = d.data[this.displayField];
18154             }
18155             
18156             var value = '';
18157             
18158             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18159                 value = d.data[this.valueField];
18160             }
18161             
18162             var option = {
18163                 tag: 'option',
18164                 value : value,
18165                 html : html
18166             };
18167             
18168             if(this.value == d.data[this.valueField]){
18169                 option['selected'] = true;
18170             }
18171             
18172             var opt = this.inputEl().createChild(option);
18173             
18174             this.ios_options.push({
18175                 data : d.data,
18176                 el : opt
18177             });
18178             
18179         }, this);
18180         
18181         this.inputEl().on('change', function(){
18182            this.fireEvent('select', this);
18183         }, this);
18184         
18185     },
18186     
18187     clearIOSView: function()
18188     {
18189         this.inputEl().dom.innerHTML = '';
18190         
18191         this.ios_options = [];
18192     },
18193     
18194     setIOSValue: function(v)
18195     {
18196         this.value = v;
18197         
18198         if(!this.ios_options){
18199             return;
18200         }
18201         
18202         Roo.each(this.ios_options, function(opts){
18203            
18204            opts.el.dom.removeAttribute('selected');
18205            
18206            if(opts.data[this.valueField] != v){
18207                return;
18208            }
18209            
18210            opts.el.dom.setAttribute('selected', true);
18211            
18212         }, this);
18213     }
18214
18215     /** 
18216     * @cfg {Boolean} grow 
18217     * @hide 
18218     */
18219     /** 
18220     * @cfg {Number} growMin 
18221     * @hide 
18222     */
18223     /** 
18224     * @cfg {Number} growMax 
18225     * @hide 
18226     */
18227     /**
18228      * @hide
18229      * @method autoSize
18230      */
18231 });
18232
18233 Roo.apply(Roo.bootstrap.ComboBox,  {
18234     
18235     header : {
18236         tag: 'div',
18237         cls: 'modal-header',
18238         cn: [
18239             {
18240                 tag: 'h4',
18241                 cls: 'modal-title'
18242             }
18243         ]
18244     },
18245     
18246     body : {
18247         tag: 'div',
18248         cls: 'modal-body',
18249         cn: [
18250             {
18251                 tag: 'ul',
18252                 cls: 'list-group'
18253             }
18254         ]
18255     },
18256     
18257     listItemRadio : {
18258         tag: 'li',
18259         cls: 'list-group-item',
18260         cn: [
18261             {
18262                 tag: 'span',
18263                 cls: 'roo-combobox-list-group-item-value'
18264             },
18265             {
18266                 tag: 'div',
18267                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18268                 cn: [
18269                     {
18270                         tag: 'input',
18271                         type: 'radio'
18272                     },
18273                     {
18274                         tag: 'label'
18275                     }
18276                 ]
18277             }
18278         ]
18279     },
18280     
18281     listItemCheckbox : {
18282         tag: 'li',
18283         cls: 'list-group-item',
18284         cn: [
18285             {
18286                 tag: 'span',
18287                 cls: 'roo-combobox-list-group-item-value'
18288             },
18289             {
18290                 tag: 'div',
18291                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18292                 cn: [
18293                     {
18294                         tag: 'input',
18295                         type: 'checkbox'
18296                     },
18297                     {
18298                         tag: 'label'
18299                     }
18300                 ]
18301             }
18302         ]
18303     },
18304     
18305     emptyResult : {
18306         tag: 'div',
18307         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18308     },
18309     
18310     footer : {
18311         tag: 'div',
18312         cls: 'modal-footer',
18313         cn: [
18314             {
18315                 tag: 'div',
18316                 cls: 'row',
18317                 cn: [
18318                     {
18319                         tag: 'div',
18320                         cls: 'col-xs-6 text-left',
18321                         cn: {
18322                             tag: 'button',
18323                             cls: 'btn btn-danger roo-touch-view-cancel',
18324                             html: 'Cancel'
18325                         }
18326                     },
18327                     {
18328                         tag: 'div',
18329                         cls: 'col-xs-6 text-right',
18330                         cn: {
18331                             tag: 'button',
18332                             cls: 'btn btn-success roo-touch-view-ok',
18333                             html: 'OK'
18334                         }
18335                     }
18336                 ]
18337             }
18338         ]
18339         
18340     }
18341 });
18342
18343 Roo.apply(Roo.bootstrap.ComboBox,  {
18344     
18345     touchViewTemplate : {
18346         tag: 'div',
18347         cls: 'modal fade roo-combobox-touch-view',
18348         cn: [
18349             {
18350                 tag: 'div',
18351                 cls: 'modal-dialog',
18352                 style : 'position:fixed', // we have to fix position....
18353                 cn: [
18354                     {
18355                         tag: 'div',
18356                         cls: 'modal-content',
18357                         cn: [
18358                             Roo.bootstrap.ComboBox.header,
18359                             Roo.bootstrap.ComboBox.body,
18360                             Roo.bootstrap.ComboBox.footer
18361                         ]
18362                     }
18363                 ]
18364             }
18365         ]
18366     }
18367 });/*
18368  * Based on:
18369  * Ext JS Library 1.1.1
18370  * Copyright(c) 2006-2007, Ext JS, LLC.
18371  *
18372  * Originally Released Under LGPL - original licence link has changed is not relivant.
18373  *
18374  * Fork - LGPL
18375  * <script type="text/javascript">
18376  */
18377
18378 /**
18379  * @class Roo.View
18380  * @extends Roo.util.Observable
18381  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18382  * This class also supports single and multi selection modes. <br>
18383  * Create a data model bound view:
18384  <pre><code>
18385  var store = new Roo.data.Store(...);
18386
18387  var view = new Roo.View({
18388     el : "my-element",
18389     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18390  
18391     singleSelect: true,
18392     selectedClass: "ydataview-selected",
18393     store: store
18394  });
18395
18396  // listen for node click?
18397  view.on("click", function(vw, index, node, e){
18398  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18399  });
18400
18401  // load XML data
18402  dataModel.load("foobar.xml");
18403  </code></pre>
18404  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18405  * <br><br>
18406  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18407  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18408  * 
18409  * Note: old style constructor is still suported (container, template, config)
18410  * 
18411  * @constructor
18412  * Create a new View
18413  * @param {Object} config The config object
18414  * 
18415  */
18416 Roo.View = function(config, depreciated_tpl, depreciated_config){
18417     
18418     this.parent = false;
18419     
18420     if (typeof(depreciated_tpl) == 'undefined') {
18421         // new way.. - universal constructor.
18422         Roo.apply(this, config);
18423         this.el  = Roo.get(this.el);
18424     } else {
18425         // old format..
18426         this.el  = Roo.get(config);
18427         this.tpl = depreciated_tpl;
18428         Roo.apply(this, depreciated_config);
18429     }
18430     this.wrapEl  = this.el.wrap().wrap();
18431     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18432     
18433     
18434     if(typeof(this.tpl) == "string"){
18435         this.tpl = new Roo.Template(this.tpl);
18436     } else {
18437         // support xtype ctors..
18438         this.tpl = new Roo.factory(this.tpl, Roo);
18439     }
18440     
18441     
18442     this.tpl.compile();
18443     
18444     /** @private */
18445     this.addEvents({
18446         /**
18447          * @event beforeclick
18448          * Fires before a click is processed. Returns false to cancel the default action.
18449          * @param {Roo.View} this
18450          * @param {Number} index The index of the target node
18451          * @param {HTMLElement} node The target node
18452          * @param {Roo.EventObject} e The raw event object
18453          */
18454             "beforeclick" : true,
18455         /**
18456          * @event click
18457          * Fires when a template node is clicked.
18458          * @param {Roo.View} this
18459          * @param {Number} index The index of the target node
18460          * @param {HTMLElement} node The target node
18461          * @param {Roo.EventObject} e The raw event object
18462          */
18463             "click" : true,
18464         /**
18465          * @event dblclick
18466          * Fires when a template node is double clicked.
18467          * @param {Roo.View} this
18468          * @param {Number} index The index of the target node
18469          * @param {HTMLElement} node The target node
18470          * @param {Roo.EventObject} e The raw event object
18471          */
18472             "dblclick" : true,
18473         /**
18474          * @event contextmenu
18475          * Fires when a template node is right clicked.
18476          * @param {Roo.View} this
18477          * @param {Number} index The index of the target node
18478          * @param {HTMLElement} node The target node
18479          * @param {Roo.EventObject} e The raw event object
18480          */
18481             "contextmenu" : true,
18482         /**
18483          * @event selectionchange
18484          * Fires when the selected nodes change.
18485          * @param {Roo.View} this
18486          * @param {Array} selections Array of the selected nodes
18487          */
18488             "selectionchange" : true,
18489     
18490         /**
18491          * @event beforeselect
18492          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18493          * @param {Roo.View} this
18494          * @param {HTMLElement} node The node to be selected
18495          * @param {Array} selections Array of currently selected nodes
18496          */
18497             "beforeselect" : true,
18498         /**
18499          * @event preparedata
18500          * Fires on every row to render, to allow you to change the data.
18501          * @param {Roo.View} this
18502          * @param {Object} data to be rendered (change this)
18503          */
18504           "preparedata" : true
18505           
18506           
18507         });
18508
18509
18510
18511     this.el.on({
18512         "click": this.onClick,
18513         "dblclick": this.onDblClick,
18514         "contextmenu": this.onContextMenu,
18515         scope:this
18516     });
18517
18518     this.selections = [];
18519     this.nodes = [];
18520     this.cmp = new Roo.CompositeElementLite([]);
18521     if(this.store){
18522         this.store = Roo.factory(this.store, Roo.data);
18523         this.setStore(this.store, true);
18524     }
18525     
18526     if ( this.footer && this.footer.xtype) {
18527            
18528          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18529         
18530         this.footer.dataSource = this.store;
18531         this.footer.container = fctr;
18532         this.footer = Roo.factory(this.footer, Roo);
18533         fctr.insertFirst(this.el);
18534         
18535         // this is a bit insane - as the paging toolbar seems to detach the el..
18536 //        dom.parentNode.parentNode.parentNode
18537          // they get detached?
18538     }
18539     
18540     
18541     Roo.View.superclass.constructor.call(this);
18542     
18543     
18544 };
18545
18546 Roo.extend(Roo.View, Roo.util.Observable, {
18547     
18548      /**
18549      * @cfg {Roo.data.Store} store Data store to load data from.
18550      */
18551     store : false,
18552     
18553     /**
18554      * @cfg {String|Roo.Element} el The container element.
18555      */
18556     el : '',
18557     
18558     /**
18559      * @cfg {String|Roo.Template} tpl The template used by this View 
18560      */
18561     tpl : false,
18562     /**
18563      * @cfg {String} dataName the named area of the template to use as the data area
18564      *                          Works with domtemplates roo-name="name"
18565      */
18566     dataName: false,
18567     /**
18568      * @cfg {String} selectedClass The css class to add to selected nodes
18569      */
18570     selectedClass : "x-view-selected",
18571      /**
18572      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18573      */
18574     emptyText : "",
18575     
18576     /**
18577      * @cfg {String} text to display on mask (default Loading)
18578      */
18579     mask : false,
18580     /**
18581      * @cfg {Boolean} multiSelect Allow multiple selection
18582      */
18583     multiSelect : false,
18584     /**
18585      * @cfg {Boolean} singleSelect Allow single selection
18586      */
18587     singleSelect:  false,
18588     
18589     /**
18590      * @cfg {Boolean} toggleSelect - selecting 
18591      */
18592     toggleSelect : false,
18593     
18594     /**
18595      * @cfg {Boolean} tickable - selecting 
18596      */
18597     tickable : false,
18598     
18599     /**
18600      * Returns the element this view is bound to.
18601      * @return {Roo.Element}
18602      */
18603     getEl : function(){
18604         return this.wrapEl;
18605     },
18606     
18607     
18608
18609     /**
18610      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18611      */
18612     refresh : function(){
18613         //Roo.log('refresh');
18614         var t = this.tpl;
18615         
18616         // if we are using something like 'domtemplate', then
18617         // the what gets used is:
18618         // t.applySubtemplate(NAME, data, wrapping data..)
18619         // the outer template then get' applied with
18620         //     the store 'extra data'
18621         // and the body get's added to the
18622         //      roo-name="data" node?
18623         //      <span class='roo-tpl-{name}'></span> ?????
18624         
18625         
18626         
18627         this.clearSelections();
18628         this.el.update("");
18629         var html = [];
18630         var records = this.store.getRange();
18631         if(records.length < 1) {
18632             
18633             // is this valid??  = should it render a template??
18634             
18635             this.el.update(this.emptyText);
18636             return;
18637         }
18638         var el = this.el;
18639         if (this.dataName) {
18640             this.el.update(t.apply(this.store.meta)); //????
18641             el = this.el.child('.roo-tpl-' + this.dataName);
18642         }
18643         
18644         for(var i = 0, len = records.length; i < len; i++){
18645             var data = this.prepareData(records[i].data, i, records[i]);
18646             this.fireEvent("preparedata", this, data, i, records[i]);
18647             
18648             var d = Roo.apply({}, data);
18649             
18650             if(this.tickable){
18651                 Roo.apply(d, {'roo-id' : Roo.id()});
18652                 
18653                 var _this = this;
18654             
18655                 Roo.each(this.parent.item, function(item){
18656                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18657                         return;
18658                     }
18659                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18660                 });
18661             }
18662             
18663             html[html.length] = Roo.util.Format.trim(
18664                 this.dataName ?
18665                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18666                     t.apply(d)
18667             );
18668         }
18669         
18670         
18671         
18672         el.update(html.join(""));
18673         this.nodes = el.dom.childNodes;
18674         this.updateIndexes(0);
18675     },
18676     
18677
18678     /**
18679      * Function to override to reformat the data that is sent to
18680      * the template for each node.
18681      * DEPRICATED - use the preparedata event handler.
18682      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18683      * a JSON object for an UpdateManager bound view).
18684      */
18685     prepareData : function(data, index, record)
18686     {
18687         this.fireEvent("preparedata", this, data, index, record);
18688         return data;
18689     },
18690
18691     onUpdate : function(ds, record){
18692         // Roo.log('on update');   
18693         this.clearSelections();
18694         var index = this.store.indexOf(record);
18695         var n = this.nodes[index];
18696         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18697         n.parentNode.removeChild(n);
18698         this.updateIndexes(index, index);
18699     },
18700
18701     
18702     
18703 // --------- FIXME     
18704     onAdd : function(ds, records, index)
18705     {
18706         //Roo.log(['on Add', ds, records, index] );        
18707         this.clearSelections();
18708         if(this.nodes.length == 0){
18709             this.refresh();
18710             return;
18711         }
18712         var n = this.nodes[index];
18713         for(var i = 0, len = records.length; i < len; i++){
18714             var d = this.prepareData(records[i].data, i, records[i]);
18715             if(n){
18716                 this.tpl.insertBefore(n, d);
18717             }else{
18718                 
18719                 this.tpl.append(this.el, d);
18720             }
18721         }
18722         this.updateIndexes(index);
18723     },
18724
18725     onRemove : function(ds, record, index){
18726        // Roo.log('onRemove');
18727         this.clearSelections();
18728         var el = this.dataName  ?
18729             this.el.child('.roo-tpl-' + this.dataName) :
18730             this.el; 
18731         
18732         el.dom.removeChild(this.nodes[index]);
18733         this.updateIndexes(index);
18734     },
18735
18736     /**
18737      * Refresh an individual node.
18738      * @param {Number} index
18739      */
18740     refreshNode : function(index){
18741         this.onUpdate(this.store, this.store.getAt(index));
18742     },
18743
18744     updateIndexes : function(startIndex, endIndex){
18745         var ns = this.nodes;
18746         startIndex = startIndex || 0;
18747         endIndex = endIndex || ns.length - 1;
18748         for(var i = startIndex; i <= endIndex; i++){
18749             ns[i].nodeIndex = i;
18750         }
18751     },
18752
18753     /**
18754      * Changes the data store this view uses and refresh the view.
18755      * @param {Store} store
18756      */
18757     setStore : function(store, initial){
18758         if(!initial && this.store){
18759             this.store.un("datachanged", this.refresh);
18760             this.store.un("add", this.onAdd);
18761             this.store.un("remove", this.onRemove);
18762             this.store.un("update", this.onUpdate);
18763             this.store.un("clear", this.refresh);
18764             this.store.un("beforeload", this.onBeforeLoad);
18765             this.store.un("load", this.onLoad);
18766             this.store.un("loadexception", this.onLoad);
18767         }
18768         if(store){
18769           
18770             store.on("datachanged", this.refresh, this);
18771             store.on("add", this.onAdd, this);
18772             store.on("remove", this.onRemove, this);
18773             store.on("update", this.onUpdate, this);
18774             store.on("clear", this.refresh, this);
18775             store.on("beforeload", this.onBeforeLoad, this);
18776             store.on("load", this.onLoad, this);
18777             store.on("loadexception", this.onLoad, this);
18778         }
18779         
18780         if(store){
18781             this.refresh();
18782         }
18783     },
18784     /**
18785      * onbeforeLoad - masks the loading area.
18786      *
18787      */
18788     onBeforeLoad : function(store,opts)
18789     {
18790          //Roo.log('onBeforeLoad');   
18791         if (!opts.add) {
18792             this.el.update("");
18793         }
18794         this.el.mask(this.mask ? this.mask : "Loading" ); 
18795     },
18796     onLoad : function ()
18797     {
18798         this.el.unmask();
18799     },
18800     
18801
18802     /**
18803      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18804      * @param {HTMLElement} node
18805      * @return {HTMLElement} The template node
18806      */
18807     findItemFromChild : function(node){
18808         var el = this.dataName  ?
18809             this.el.child('.roo-tpl-' + this.dataName,true) :
18810             this.el.dom; 
18811         
18812         if(!node || node.parentNode == el){
18813                     return node;
18814             }
18815             var p = node.parentNode;
18816             while(p && p != el){
18817             if(p.parentNode == el){
18818                 return p;
18819             }
18820             p = p.parentNode;
18821         }
18822             return null;
18823     },
18824
18825     /** @ignore */
18826     onClick : function(e){
18827         var item = this.findItemFromChild(e.getTarget());
18828         if(item){
18829             var index = this.indexOf(item);
18830             if(this.onItemClick(item, index, e) !== false){
18831                 this.fireEvent("click", this, index, item, e);
18832             }
18833         }else{
18834             this.clearSelections();
18835         }
18836     },
18837
18838     /** @ignore */
18839     onContextMenu : function(e){
18840         var item = this.findItemFromChild(e.getTarget());
18841         if(item){
18842             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18843         }
18844     },
18845
18846     /** @ignore */
18847     onDblClick : function(e){
18848         var item = this.findItemFromChild(e.getTarget());
18849         if(item){
18850             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18851         }
18852     },
18853
18854     onItemClick : function(item, index, e)
18855     {
18856         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18857             return false;
18858         }
18859         if (this.toggleSelect) {
18860             var m = this.isSelected(item) ? 'unselect' : 'select';
18861             //Roo.log(m);
18862             var _t = this;
18863             _t[m](item, true, false);
18864             return true;
18865         }
18866         if(this.multiSelect || this.singleSelect){
18867             if(this.multiSelect && e.shiftKey && this.lastSelection){
18868                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18869             }else{
18870                 this.select(item, this.multiSelect && e.ctrlKey);
18871                 this.lastSelection = item;
18872             }
18873             
18874             if(!this.tickable){
18875                 e.preventDefault();
18876             }
18877             
18878         }
18879         return true;
18880     },
18881
18882     /**
18883      * Get the number of selected nodes.
18884      * @return {Number}
18885      */
18886     getSelectionCount : function(){
18887         return this.selections.length;
18888     },
18889
18890     /**
18891      * Get the currently selected nodes.
18892      * @return {Array} An array of HTMLElements
18893      */
18894     getSelectedNodes : function(){
18895         return this.selections;
18896     },
18897
18898     /**
18899      * Get the indexes of the selected nodes.
18900      * @return {Array}
18901      */
18902     getSelectedIndexes : function(){
18903         var indexes = [], s = this.selections;
18904         for(var i = 0, len = s.length; i < len; i++){
18905             indexes.push(s[i].nodeIndex);
18906         }
18907         return indexes;
18908     },
18909
18910     /**
18911      * Clear all selections
18912      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18913      */
18914     clearSelections : function(suppressEvent){
18915         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18916             this.cmp.elements = this.selections;
18917             this.cmp.removeClass(this.selectedClass);
18918             this.selections = [];
18919             if(!suppressEvent){
18920                 this.fireEvent("selectionchange", this, this.selections);
18921             }
18922         }
18923     },
18924
18925     /**
18926      * Returns true if the passed node is selected
18927      * @param {HTMLElement/Number} node The node or node index
18928      * @return {Boolean}
18929      */
18930     isSelected : function(node){
18931         var s = this.selections;
18932         if(s.length < 1){
18933             return false;
18934         }
18935         node = this.getNode(node);
18936         return s.indexOf(node) !== -1;
18937     },
18938
18939     /**
18940      * Selects nodes.
18941      * @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
18942      * @param {Boolean} keepExisting (optional) true to keep existing selections
18943      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18944      */
18945     select : function(nodeInfo, keepExisting, suppressEvent){
18946         if(nodeInfo instanceof Array){
18947             if(!keepExisting){
18948                 this.clearSelections(true);
18949             }
18950             for(var i = 0, len = nodeInfo.length; i < len; i++){
18951                 this.select(nodeInfo[i], true, true);
18952             }
18953             return;
18954         } 
18955         var node = this.getNode(nodeInfo);
18956         if(!node || this.isSelected(node)){
18957             return; // already selected.
18958         }
18959         if(!keepExisting){
18960             this.clearSelections(true);
18961         }
18962         
18963         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18964             Roo.fly(node).addClass(this.selectedClass);
18965             this.selections.push(node);
18966             if(!suppressEvent){
18967                 this.fireEvent("selectionchange", this, this.selections);
18968             }
18969         }
18970         
18971         
18972     },
18973       /**
18974      * Unselects nodes.
18975      * @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
18976      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18977      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18978      */
18979     unselect : function(nodeInfo, keepExisting, suppressEvent)
18980     {
18981         if(nodeInfo instanceof Array){
18982             Roo.each(this.selections, function(s) {
18983                 this.unselect(s, nodeInfo);
18984             }, this);
18985             return;
18986         }
18987         var node = this.getNode(nodeInfo);
18988         if(!node || !this.isSelected(node)){
18989             //Roo.log("not selected");
18990             return; // not selected.
18991         }
18992         // fireevent???
18993         var ns = [];
18994         Roo.each(this.selections, function(s) {
18995             if (s == node ) {
18996                 Roo.fly(node).removeClass(this.selectedClass);
18997
18998                 return;
18999             }
19000             ns.push(s);
19001         },this);
19002         
19003         this.selections= ns;
19004         this.fireEvent("selectionchange", this, this.selections);
19005     },
19006
19007     /**
19008      * Gets a template node.
19009      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19010      * @return {HTMLElement} The node or null if it wasn't found
19011      */
19012     getNode : function(nodeInfo){
19013         if(typeof nodeInfo == "string"){
19014             return document.getElementById(nodeInfo);
19015         }else if(typeof nodeInfo == "number"){
19016             return this.nodes[nodeInfo];
19017         }
19018         return nodeInfo;
19019     },
19020
19021     /**
19022      * Gets a range template nodes.
19023      * @param {Number} startIndex
19024      * @param {Number} endIndex
19025      * @return {Array} An array of nodes
19026      */
19027     getNodes : function(start, end){
19028         var ns = this.nodes;
19029         start = start || 0;
19030         end = typeof end == "undefined" ? ns.length - 1 : end;
19031         var nodes = [];
19032         if(start <= end){
19033             for(var i = start; i <= end; i++){
19034                 nodes.push(ns[i]);
19035             }
19036         } else{
19037             for(var i = start; i >= end; i--){
19038                 nodes.push(ns[i]);
19039             }
19040         }
19041         return nodes;
19042     },
19043
19044     /**
19045      * Finds the index of the passed node
19046      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19047      * @return {Number} The index of the node or -1
19048      */
19049     indexOf : function(node){
19050         node = this.getNode(node);
19051         if(typeof node.nodeIndex == "number"){
19052             return node.nodeIndex;
19053         }
19054         var ns = this.nodes;
19055         for(var i = 0, len = ns.length; i < len; i++){
19056             if(ns[i] == node){
19057                 return i;
19058             }
19059         }
19060         return -1;
19061     }
19062 });
19063 /*
19064  * - LGPL
19065  *
19066  * based on jquery fullcalendar
19067  * 
19068  */
19069
19070 Roo.bootstrap = Roo.bootstrap || {};
19071 /**
19072  * @class Roo.bootstrap.Calendar
19073  * @extends Roo.bootstrap.Component
19074  * Bootstrap Calendar class
19075  * @cfg {Boolean} loadMask (true|false) default false
19076  * @cfg {Object} header generate the user specific header of the calendar, default false
19077
19078  * @constructor
19079  * Create a new Container
19080  * @param {Object} config The config object
19081  */
19082
19083
19084
19085 Roo.bootstrap.Calendar = function(config){
19086     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19087      this.addEvents({
19088         /**
19089              * @event select
19090              * Fires when a date is selected
19091              * @param {DatePicker} this
19092              * @param {Date} date The selected date
19093              */
19094         'select': true,
19095         /**
19096              * @event monthchange
19097              * Fires when the displayed month changes 
19098              * @param {DatePicker} this
19099              * @param {Date} date The selected month
19100              */
19101         'monthchange': true,
19102         /**
19103              * @event evententer
19104              * Fires when mouse over an event
19105              * @param {Calendar} this
19106              * @param {event} Event
19107              */
19108         'evententer': true,
19109         /**
19110              * @event eventleave
19111              * Fires when the mouse leaves an
19112              * @param {Calendar} this
19113              * @param {event}
19114              */
19115         'eventleave': true,
19116         /**
19117              * @event eventclick
19118              * Fires when the mouse click an
19119              * @param {Calendar} this
19120              * @param {event}
19121              */
19122         'eventclick': true
19123         
19124     });
19125
19126 };
19127
19128 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19129     
19130      /**
19131      * @cfg {Number} startDay
19132      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19133      */
19134     startDay : 0,
19135     
19136     loadMask : false,
19137     
19138     header : false,
19139       
19140     getAutoCreate : function(){
19141         
19142         
19143         var fc_button = function(name, corner, style, content ) {
19144             return Roo.apply({},{
19145                 tag : 'span',
19146                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19147                          (corner.length ?
19148                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19149                             ''
19150                         ),
19151                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19152                 unselectable: 'on'
19153             });
19154         };
19155         
19156         var header = {};
19157         
19158         if(!this.header){
19159             header = {
19160                 tag : 'table',
19161                 cls : 'fc-header',
19162                 style : 'width:100%',
19163                 cn : [
19164                     {
19165                         tag: 'tr',
19166                         cn : [
19167                             {
19168                                 tag : 'td',
19169                                 cls : 'fc-header-left',
19170                                 cn : [
19171                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19172                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19173                                     { tag: 'span', cls: 'fc-header-space' },
19174                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19175
19176
19177                                 ]
19178                             },
19179
19180                             {
19181                                 tag : 'td',
19182                                 cls : 'fc-header-center',
19183                                 cn : [
19184                                     {
19185                                         tag: 'span',
19186                                         cls: 'fc-header-title',
19187                                         cn : {
19188                                             tag: 'H2',
19189                                             html : 'month / year'
19190                                         }
19191                                     }
19192
19193                                 ]
19194                             },
19195                             {
19196                                 tag : 'td',
19197                                 cls : 'fc-header-right',
19198                                 cn : [
19199                               /*      fc_button('month', 'left', '', 'month' ),
19200                                     fc_button('week', '', '', 'week' ),
19201                                     fc_button('day', 'right', '', 'day' )
19202                                 */    
19203
19204                                 ]
19205                             }
19206
19207                         ]
19208                     }
19209                 ]
19210             };
19211         }
19212         
19213         header = this.header;
19214         
19215        
19216         var cal_heads = function() {
19217             var ret = [];
19218             // fixme - handle this.
19219             
19220             for (var i =0; i < Date.dayNames.length; i++) {
19221                 var d = Date.dayNames[i];
19222                 ret.push({
19223                     tag: 'th',
19224                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19225                     html : d.substring(0,3)
19226                 });
19227                 
19228             }
19229             ret[0].cls += ' fc-first';
19230             ret[6].cls += ' fc-last';
19231             return ret;
19232         };
19233         var cal_cell = function(n) {
19234             return  {
19235                 tag: 'td',
19236                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19237                 cn : [
19238                     {
19239                         cn : [
19240                             {
19241                                 cls: 'fc-day-number',
19242                                 html: 'D'
19243                             },
19244                             {
19245                                 cls: 'fc-day-content',
19246                              
19247                                 cn : [
19248                                      {
19249                                         style: 'position: relative;' // height: 17px;
19250                                     }
19251                                 ]
19252                             }
19253                             
19254                             
19255                         ]
19256                     }
19257                 ]
19258                 
19259             }
19260         };
19261         var cal_rows = function() {
19262             
19263             var ret = [];
19264             for (var r = 0; r < 6; r++) {
19265                 var row= {
19266                     tag : 'tr',
19267                     cls : 'fc-week',
19268                     cn : []
19269                 };
19270                 
19271                 for (var i =0; i < Date.dayNames.length; i++) {
19272                     var d = Date.dayNames[i];
19273                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19274
19275                 }
19276                 row.cn[0].cls+=' fc-first';
19277                 row.cn[0].cn[0].style = 'min-height:90px';
19278                 row.cn[6].cls+=' fc-last';
19279                 ret.push(row);
19280                 
19281             }
19282             ret[0].cls += ' fc-first';
19283             ret[4].cls += ' fc-prev-last';
19284             ret[5].cls += ' fc-last';
19285             return ret;
19286             
19287         };
19288         
19289         var cal_table = {
19290             tag: 'table',
19291             cls: 'fc-border-separate',
19292             style : 'width:100%',
19293             cellspacing  : 0,
19294             cn : [
19295                 { 
19296                     tag: 'thead',
19297                     cn : [
19298                         { 
19299                             tag: 'tr',
19300                             cls : 'fc-first fc-last',
19301                             cn : cal_heads()
19302                         }
19303                     ]
19304                 },
19305                 { 
19306                     tag: 'tbody',
19307                     cn : cal_rows()
19308                 }
19309                   
19310             ]
19311         };
19312          
19313          var cfg = {
19314             cls : 'fc fc-ltr',
19315             cn : [
19316                 header,
19317                 {
19318                     cls : 'fc-content',
19319                     style : "position: relative;",
19320                     cn : [
19321                         {
19322                             cls : 'fc-view fc-view-month fc-grid',
19323                             style : 'position: relative',
19324                             unselectable : 'on',
19325                             cn : [
19326                                 {
19327                                     cls : 'fc-event-container',
19328                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19329                                 },
19330                                 cal_table
19331                             ]
19332                         }
19333                     ]
19334     
19335                 }
19336            ] 
19337             
19338         };
19339         
19340          
19341         
19342         return cfg;
19343     },
19344     
19345     
19346     initEvents : function()
19347     {
19348         if(!this.store){
19349             throw "can not find store for calendar";
19350         }
19351         
19352         var mark = {
19353             tag: "div",
19354             cls:"x-dlg-mask",
19355             style: "text-align:center",
19356             cn: [
19357                 {
19358                     tag: "div",
19359                     style: "background-color:white;width:50%;margin:250 auto",
19360                     cn: [
19361                         {
19362                             tag: "img",
19363                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19364                         },
19365                         {
19366                             tag: "span",
19367                             html: "Loading"
19368                         }
19369                         
19370                     ]
19371                 }
19372             ]
19373         };
19374         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19375         
19376         var size = this.el.select('.fc-content', true).first().getSize();
19377         this.maskEl.setSize(size.width, size.height);
19378         this.maskEl.enableDisplayMode("block");
19379         if(!this.loadMask){
19380             this.maskEl.hide();
19381         }
19382         
19383         this.store = Roo.factory(this.store, Roo.data);
19384         this.store.on('load', this.onLoad, this);
19385         this.store.on('beforeload', this.onBeforeLoad, this);
19386         
19387         this.resize();
19388         
19389         this.cells = this.el.select('.fc-day',true);
19390         //Roo.log(this.cells);
19391         this.textNodes = this.el.query('.fc-day-number');
19392         this.cells.addClassOnOver('fc-state-hover');
19393         
19394         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19395         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19396         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19397         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19398         
19399         this.on('monthchange', this.onMonthChange, this);
19400         
19401         this.update(new Date().clearTime());
19402     },
19403     
19404     resize : function() {
19405         var sz  = this.el.getSize();
19406         
19407         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19408         this.el.select('.fc-day-content div',true).setHeight(34);
19409     },
19410     
19411     
19412     // private
19413     showPrevMonth : function(e){
19414         this.update(this.activeDate.add("mo", -1));
19415     },
19416     showToday : function(e){
19417         this.update(new Date().clearTime());
19418     },
19419     // private
19420     showNextMonth : function(e){
19421         this.update(this.activeDate.add("mo", 1));
19422     },
19423
19424     // private
19425     showPrevYear : function(){
19426         this.update(this.activeDate.add("y", -1));
19427     },
19428
19429     // private
19430     showNextYear : function(){
19431         this.update(this.activeDate.add("y", 1));
19432     },
19433
19434     
19435    // private
19436     update : function(date)
19437     {
19438         var vd = this.activeDate;
19439         this.activeDate = date;
19440 //        if(vd && this.el){
19441 //            var t = date.getTime();
19442 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19443 //                Roo.log('using add remove');
19444 //                
19445 //                this.fireEvent('monthchange', this, date);
19446 //                
19447 //                this.cells.removeClass("fc-state-highlight");
19448 //                this.cells.each(function(c){
19449 //                   if(c.dateValue == t){
19450 //                       c.addClass("fc-state-highlight");
19451 //                       setTimeout(function(){
19452 //                            try{c.dom.firstChild.focus();}catch(e){}
19453 //                       }, 50);
19454 //                       return false;
19455 //                   }
19456 //                   return true;
19457 //                });
19458 //                return;
19459 //            }
19460 //        }
19461         
19462         var days = date.getDaysInMonth();
19463         
19464         var firstOfMonth = date.getFirstDateOfMonth();
19465         var startingPos = firstOfMonth.getDay()-this.startDay;
19466         
19467         if(startingPos < this.startDay){
19468             startingPos += 7;
19469         }
19470         
19471         var pm = date.add(Date.MONTH, -1);
19472         var prevStart = pm.getDaysInMonth()-startingPos;
19473 //        
19474         this.cells = this.el.select('.fc-day',true);
19475         this.textNodes = this.el.query('.fc-day-number');
19476         this.cells.addClassOnOver('fc-state-hover');
19477         
19478         var cells = this.cells.elements;
19479         var textEls = this.textNodes;
19480         
19481         Roo.each(cells, function(cell){
19482             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19483         });
19484         
19485         days += startingPos;
19486
19487         // convert everything to numbers so it's fast
19488         var day = 86400000;
19489         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19490         //Roo.log(d);
19491         //Roo.log(pm);
19492         //Roo.log(prevStart);
19493         
19494         var today = new Date().clearTime().getTime();
19495         var sel = date.clearTime().getTime();
19496         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19497         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19498         var ddMatch = this.disabledDatesRE;
19499         var ddText = this.disabledDatesText;
19500         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19501         var ddaysText = this.disabledDaysText;
19502         var format = this.format;
19503         
19504         var setCellClass = function(cal, cell){
19505             cell.row = 0;
19506             cell.events = [];
19507             cell.more = [];
19508             //Roo.log('set Cell Class');
19509             cell.title = "";
19510             var t = d.getTime();
19511             
19512             //Roo.log(d);
19513             
19514             cell.dateValue = t;
19515             if(t == today){
19516                 cell.className += " fc-today";
19517                 cell.className += " fc-state-highlight";
19518                 cell.title = cal.todayText;
19519             }
19520             if(t == sel){
19521                 // disable highlight in other month..
19522                 //cell.className += " fc-state-highlight";
19523                 
19524             }
19525             // disabling
19526             if(t < min) {
19527                 cell.className = " fc-state-disabled";
19528                 cell.title = cal.minText;
19529                 return;
19530             }
19531             if(t > max) {
19532                 cell.className = " fc-state-disabled";
19533                 cell.title = cal.maxText;
19534                 return;
19535             }
19536             if(ddays){
19537                 if(ddays.indexOf(d.getDay()) != -1){
19538                     cell.title = ddaysText;
19539                     cell.className = " fc-state-disabled";
19540                 }
19541             }
19542             if(ddMatch && format){
19543                 var fvalue = d.dateFormat(format);
19544                 if(ddMatch.test(fvalue)){
19545                     cell.title = ddText.replace("%0", fvalue);
19546                     cell.className = " fc-state-disabled";
19547                 }
19548             }
19549             
19550             if (!cell.initialClassName) {
19551                 cell.initialClassName = cell.dom.className;
19552             }
19553             
19554             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19555         };
19556
19557         var i = 0;
19558         
19559         for(; i < startingPos; i++) {
19560             textEls[i].innerHTML = (++prevStart);
19561             d.setDate(d.getDate()+1);
19562             
19563             cells[i].className = "fc-past fc-other-month";
19564             setCellClass(this, cells[i]);
19565         }
19566         
19567         var intDay = 0;
19568         
19569         for(; i < days; i++){
19570             intDay = i - startingPos + 1;
19571             textEls[i].innerHTML = (intDay);
19572             d.setDate(d.getDate()+1);
19573             
19574             cells[i].className = ''; // "x-date-active";
19575             setCellClass(this, cells[i]);
19576         }
19577         var extraDays = 0;
19578         
19579         for(; i < 42; i++) {
19580             textEls[i].innerHTML = (++extraDays);
19581             d.setDate(d.getDate()+1);
19582             
19583             cells[i].className = "fc-future fc-other-month";
19584             setCellClass(this, cells[i]);
19585         }
19586         
19587         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19588         
19589         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19590         
19591         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19592         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19593         
19594         if(totalRows != 6){
19595             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19596             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19597         }
19598         
19599         this.fireEvent('monthchange', this, date);
19600         
19601         
19602         /*
19603         if(!this.internalRender){
19604             var main = this.el.dom.firstChild;
19605             var w = main.offsetWidth;
19606             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19607             Roo.fly(main).setWidth(w);
19608             this.internalRender = true;
19609             // opera does not respect the auto grow header center column
19610             // then, after it gets a width opera refuses to recalculate
19611             // without a second pass
19612             if(Roo.isOpera && !this.secondPass){
19613                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19614                 this.secondPass = true;
19615                 this.update.defer(10, this, [date]);
19616             }
19617         }
19618         */
19619         
19620     },
19621     
19622     findCell : function(dt) {
19623         dt = dt.clearTime().getTime();
19624         var ret = false;
19625         this.cells.each(function(c){
19626             //Roo.log("check " +c.dateValue + '?=' + dt);
19627             if(c.dateValue == dt){
19628                 ret = c;
19629                 return false;
19630             }
19631             return true;
19632         });
19633         
19634         return ret;
19635     },
19636     
19637     findCells : function(ev) {
19638         var s = ev.start.clone().clearTime().getTime();
19639        // Roo.log(s);
19640         var e= ev.end.clone().clearTime().getTime();
19641        // Roo.log(e);
19642         var ret = [];
19643         this.cells.each(function(c){
19644              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19645             
19646             if(c.dateValue > e){
19647                 return ;
19648             }
19649             if(c.dateValue < s){
19650                 return ;
19651             }
19652             ret.push(c);
19653         });
19654         
19655         return ret;    
19656     },
19657     
19658 //    findBestRow: function(cells)
19659 //    {
19660 //        var ret = 0;
19661 //        
19662 //        for (var i =0 ; i < cells.length;i++) {
19663 //            ret  = Math.max(cells[i].rows || 0,ret);
19664 //        }
19665 //        return ret;
19666 //        
19667 //    },
19668     
19669     
19670     addItem : function(ev)
19671     {
19672         // look for vertical location slot in
19673         var cells = this.findCells(ev);
19674         
19675 //        ev.row = this.findBestRow(cells);
19676         
19677         // work out the location.
19678         
19679         var crow = false;
19680         var rows = [];
19681         for(var i =0; i < cells.length; i++) {
19682             
19683             cells[i].row = cells[0].row;
19684             
19685             if(i == 0){
19686                 cells[i].row = cells[i].row + 1;
19687             }
19688             
19689             if (!crow) {
19690                 crow = {
19691                     start : cells[i],
19692                     end :  cells[i]
19693                 };
19694                 continue;
19695             }
19696             if (crow.start.getY() == cells[i].getY()) {
19697                 // on same row.
19698                 crow.end = cells[i];
19699                 continue;
19700             }
19701             // different row.
19702             rows.push(crow);
19703             crow = {
19704                 start: cells[i],
19705                 end : cells[i]
19706             };
19707             
19708         }
19709         
19710         rows.push(crow);
19711         ev.els = [];
19712         ev.rows = rows;
19713         ev.cells = cells;
19714         
19715         cells[0].events.push(ev);
19716         
19717         this.calevents.push(ev);
19718     },
19719     
19720     clearEvents: function() {
19721         
19722         if(!this.calevents){
19723             return;
19724         }
19725         
19726         Roo.each(this.cells.elements, function(c){
19727             c.row = 0;
19728             c.events = [];
19729             c.more = [];
19730         });
19731         
19732         Roo.each(this.calevents, function(e) {
19733             Roo.each(e.els, function(el) {
19734                 el.un('mouseenter' ,this.onEventEnter, this);
19735                 el.un('mouseleave' ,this.onEventLeave, this);
19736                 el.remove();
19737             },this);
19738         },this);
19739         
19740         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19741             e.remove();
19742         });
19743         
19744     },
19745     
19746     renderEvents: function()
19747     {   
19748         var _this = this;
19749         
19750         this.cells.each(function(c) {
19751             
19752             if(c.row < 5){
19753                 return;
19754             }
19755             
19756             var ev = c.events;
19757             
19758             var r = 4;
19759             if(c.row != c.events.length){
19760                 r = 4 - (4 - (c.row - c.events.length));
19761             }
19762             
19763             c.events = ev.slice(0, r);
19764             c.more = ev.slice(r);
19765             
19766             if(c.more.length && c.more.length == 1){
19767                 c.events.push(c.more.pop());
19768             }
19769             
19770             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19771             
19772         });
19773             
19774         this.cells.each(function(c) {
19775             
19776             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19777             
19778             
19779             for (var e = 0; e < c.events.length; e++){
19780                 var ev = c.events[e];
19781                 var rows = ev.rows;
19782                 
19783                 for(var i = 0; i < rows.length; i++) {
19784                 
19785                     // how many rows should it span..
19786
19787                     var  cfg = {
19788                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19789                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19790
19791                         unselectable : "on",
19792                         cn : [
19793                             {
19794                                 cls: 'fc-event-inner',
19795                                 cn : [
19796     //                                {
19797     //                                  tag:'span',
19798     //                                  cls: 'fc-event-time',
19799     //                                  html : cells.length > 1 ? '' : ev.time
19800     //                                },
19801                                     {
19802                                       tag:'span',
19803                                       cls: 'fc-event-title',
19804                                       html : String.format('{0}', ev.title)
19805                                     }
19806
19807
19808                                 ]
19809                             },
19810                             {
19811                                 cls: 'ui-resizable-handle ui-resizable-e',
19812                                 html : '&nbsp;&nbsp;&nbsp'
19813                             }
19814
19815                         ]
19816                     };
19817
19818                     if (i == 0) {
19819                         cfg.cls += ' fc-event-start';
19820                     }
19821                     if ((i+1) == rows.length) {
19822                         cfg.cls += ' fc-event-end';
19823                     }
19824
19825                     var ctr = _this.el.select('.fc-event-container',true).first();
19826                     var cg = ctr.createChild(cfg);
19827
19828                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19829                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19830
19831                     var r = (c.more.length) ? 1 : 0;
19832                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19833                     cg.setWidth(ebox.right - sbox.x -2);
19834
19835                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19836                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19837                     cg.on('click', _this.onEventClick, _this, ev);
19838
19839                     ev.els.push(cg);
19840                     
19841                 }
19842                 
19843             }
19844             
19845             
19846             if(c.more.length){
19847                 var  cfg = {
19848                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19849                     style : 'position: absolute',
19850                     unselectable : "on",
19851                     cn : [
19852                         {
19853                             cls: 'fc-event-inner',
19854                             cn : [
19855                                 {
19856                                   tag:'span',
19857                                   cls: 'fc-event-title',
19858                                   html : 'More'
19859                                 }
19860
19861
19862                             ]
19863                         },
19864                         {
19865                             cls: 'ui-resizable-handle ui-resizable-e',
19866                             html : '&nbsp;&nbsp;&nbsp'
19867                         }
19868
19869                     ]
19870                 };
19871
19872                 var ctr = _this.el.select('.fc-event-container',true).first();
19873                 var cg = ctr.createChild(cfg);
19874
19875                 var sbox = c.select('.fc-day-content',true).first().getBox();
19876                 var ebox = c.select('.fc-day-content',true).first().getBox();
19877                 //Roo.log(cg);
19878                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19879                 cg.setWidth(ebox.right - sbox.x -2);
19880
19881                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19882                 
19883             }
19884             
19885         });
19886         
19887         
19888         
19889     },
19890     
19891     onEventEnter: function (e, el,event,d) {
19892         this.fireEvent('evententer', this, el, event);
19893     },
19894     
19895     onEventLeave: function (e, el,event,d) {
19896         this.fireEvent('eventleave', this, el, event);
19897     },
19898     
19899     onEventClick: function (e, el,event,d) {
19900         this.fireEvent('eventclick', this, el, event);
19901     },
19902     
19903     onMonthChange: function () {
19904         this.store.load();
19905     },
19906     
19907     onMoreEventClick: function(e, el, more)
19908     {
19909         var _this = this;
19910         
19911         this.calpopover.placement = 'right';
19912         this.calpopover.setTitle('More');
19913         
19914         this.calpopover.setContent('');
19915         
19916         var ctr = this.calpopover.el.select('.popover-content', true).first();
19917         
19918         Roo.each(more, function(m){
19919             var cfg = {
19920                 cls : 'fc-event-hori fc-event-draggable',
19921                 html : m.title
19922             };
19923             var cg = ctr.createChild(cfg);
19924             
19925             cg.on('click', _this.onEventClick, _this, m);
19926         });
19927         
19928         this.calpopover.show(el);
19929         
19930         
19931     },
19932     
19933     onLoad: function () 
19934     {   
19935         this.calevents = [];
19936         var cal = this;
19937         
19938         if(this.store.getCount() > 0){
19939             this.store.data.each(function(d){
19940                cal.addItem({
19941                     id : d.data.id,
19942                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19943                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19944                     time : d.data.start_time,
19945                     title : d.data.title,
19946                     description : d.data.description,
19947                     venue : d.data.venue
19948                 });
19949             });
19950         }
19951         
19952         this.renderEvents();
19953         
19954         if(this.calevents.length && this.loadMask){
19955             this.maskEl.hide();
19956         }
19957     },
19958     
19959     onBeforeLoad: function()
19960     {
19961         this.clearEvents();
19962         if(this.loadMask){
19963             this.maskEl.show();
19964         }
19965     }
19966 });
19967
19968  
19969  /*
19970  * - LGPL
19971  *
19972  * element
19973  * 
19974  */
19975
19976 /**
19977  * @class Roo.bootstrap.Popover
19978  * @extends Roo.bootstrap.Component
19979  * Bootstrap Popover class
19980  * @cfg {String} html contents of the popover   (or false to use children..)
19981  * @cfg {String} title of popover (or false to hide)
19982  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19983  * @cfg {String} trigger click || hover (or false to trigger manually)
19984  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19985  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19986  *      - if false and it has a 'parent' then it will be automatically added to that element
19987  *      - if string - Roo.get  will be called 
19988  * @cfg {Number} delay - delay before showing
19989  
19990  * @constructor
19991  * Create a new Popover
19992  * @param {Object} config The config object
19993  */
19994
19995 Roo.bootstrap.Popover = function(config){
19996     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19997     
19998     this.addEvents({
19999         // raw events
20000          /**
20001          * @event show
20002          * After the popover show
20003          * 
20004          * @param {Roo.bootstrap.Popover} this
20005          */
20006         "show" : true,
20007         /**
20008          * @event hide
20009          * After the popover hide
20010          * 
20011          * @param {Roo.bootstrap.Popover} this
20012          */
20013         "hide" : true
20014     });
20015 };
20016
20017 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20018     
20019     title: false,
20020     html: false,
20021     
20022     placement : 'right',
20023     trigger : 'hover', // hover
20024     modal : false,
20025     delay : 0,
20026     
20027     over: false,
20028     
20029     can_build_overlaid : false,
20030     
20031     maskEl : false, // the mask element
20032     headerEl : false,
20033     contentEl : false,
20034     alignEl : false, // when show is called with an element - this get's stored.
20035     
20036     getChildContainer : function()
20037     {
20038         return this.contentEl;
20039         
20040     },
20041     getPopoverHeader : function()
20042     {
20043         this.title = true; // flag not to hide it..
20044         this.headerEl.addClass('p-0');
20045         return this.headerEl
20046     },
20047     
20048     
20049     getAutoCreate : function(){
20050          
20051         var cfg = {
20052            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20053            style: 'display:block',
20054            cn : [
20055                 {
20056                     cls : 'arrow'
20057                 },
20058                 {
20059                     cls : 'popover-inner ',
20060                     cn : [
20061                         {
20062                             tag: 'h3',
20063                             cls: 'popover-title popover-header',
20064                             html : this.title === false ? '' : this.title
20065                         },
20066                         {
20067                             cls : 'popover-content popover-body '  + (this.cls || ''),
20068                             html : this.html || ''
20069                         }
20070                     ]
20071                     
20072                 }
20073            ]
20074         };
20075         
20076         return cfg;
20077     },
20078     /**
20079      * @param {string} the title
20080      */
20081     setTitle: function(str)
20082     {
20083         this.title = str;
20084         if (this.el) {
20085             this.headerEl.dom.innerHTML = str;
20086         }
20087         
20088     },
20089     /**
20090      * @param {string} the body content
20091      */
20092     setContent: function(str)
20093     {
20094         this.html = str;
20095         if (this.contentEl) {
20096             this.contentEl.dom.innerHTML = str;
20097         }
20098         
20099     },
20100     // as it get's added to the bottom of the page.
20101     onRender : function(ct, position)
20102     {
20103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20104         
20105         
20106         
20107         if(!this.el){
20108             var cfg = Roo.apply({},  this.getAutoCreate());
20109             cfg.id = Roo.id();
20110             
20111             if (this.cls) {
20112                 cfg.cls += ' ' + this.cls;
20113             }
20114             if (this.style) {
20115                 cfg.style = this.style;
20116             }
20117             //Roo.log("adding to ");
20118             this.el = Roo.get(document.body).createChild(cfg, position);
20119 //            Roo.log(this.el);
20120         }
20121         
20122         this.contentEl = this.el.select('.popover-content',true).first();
20123         this.headerEl =  this.el.select('.popover-title',true).first();
20124         
20125         var nitems = [];
20126         if(typeof(this.items) != 'undefined'){
20127             var items = this.items;
20128             delete this.items;
20129
20130             for(var i =0;i < items.length;i++) {
20131                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20132             }
20133         }
20134
20135         this.items = nitems;
20136         
20137         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20138         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20139         
20140         
20141         
20142         this.initEvents();
20143     },
20144     
20145     resizeMask : function()
20146     {
20147         this.maskEl.setSize(
20148             Roo.lib.Dom.getViewWidth(true),
20149             Roo.lib.Dom.getViewHeight(true)
20150         );
20151     },
20152     
20153     initEvents : function()
20154     {
20155         
20156         if (!this.modal) { 
20157             Roo.bootstrap.Popover.register(this);
20158         }
20159          
20160         this.arrowEl = this.el.select('.arrow',true).first();
20161         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20162         this.el.enableDisplayMode('block');
20163         this.el.hide();
20164  
20165         
20166         if (this.over === false && !this.parent()) {
20167             return; 
20168         }
20169         if (this.triggers === false) {
20170             return;
20171         }
20172          
20173         // support parent
20174         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20175         var triggers = this.trigger ? this.trigger.split(' ') : [];
20176         Roo.each(triggers, function(trigger) {
20177         
20178             if (trigger == 'click') {
20179                 on_el.on('click', this.toggle, this);
20180             } else if (trigger != 'manual') {
20181                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20182                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20183       
20184                 on_el.on(eventIn  ,this.enter, this);
20185                 on_el.on(eventOut, this.leave, this);
20186             }
20187         }, this);
20188     },
20189     
20190     
20191     // private
20192     timeout : null,
20193     hoverState : null,
20194     
20195     toggle : function () {
20196         this.hoverState == 'in' ? this.leave() : this.enter();
20197     },
20198     
20199     enter : function () {
20200         
20201         clearTimeout(this.timeout);
20202     
20203         this.hoverState = 'in';
20204     
20205         if (!this.delay || !this.delay.show) {
20206             this.show();
20207             return;
20208         }
20209         var _t = this;
20210         this.timeout = setTimeout(function () {
20211             if (_t.hoverState == 'in') {
20212                 _t.show();
20213             }
20214         }, this.delay.show)
20215     },
20216     
20217     leave : function() {
20218         clearTimeout(this.timeout);
20219     
20220         this.hoverState = 'out';
20221     
20222         if (!this.delay || !this.delay.hide) {
20223             this.hide();
20224             return;
20225         }
20226         var _t = this;
20227         this.timeout = setTimeout(function () {
20228             if (_t.hoverState == 'out') {
20229                 _t.hide();
20230             }
20231         }, this.delay.hide)
20232     },
20233     /**
20234      * Show the popover
20235      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20236      * @param {string} (left|right|top|bottom) position
20237      */
20238     show : function (on_el, placement)
20239     {
20240         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20241         on_el = on_el || false; // default to false
20242          
20243         if (!on_el) {
20244             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20245                 on_el = this.parent().el;
20246             } else if (this.over) {
20247                 on_el = Roo.get(this.over);
20248             }
20249             
20250         }
20251         
20252         this.alignEl = Roo.get( on_el );
20253
20254         if (!this.el) {
20255             this.render(document.body);
20256         }
20257         
20258         
20259          
20260         
20261         if (this.title === false) {
20262             this.headerEl.hide();
20263         }
20264         
20265        
20266         this.el.show();
20267         this.el.dom.style.display = 'block';
20268          
20269  
20270         if (this.alignEl) {
20271             this.updatePosition(this.placement, true);
20272              
20273         } else {
20274             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20275             var es = this.el.getSize();
20276             var x = Roo.lib.Dom.getViewWidth()/2;
20277             var y = Roo.lib.Dom.getViewHeight()/2;
20278             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20279             
20280         }
20281
20282         
20283         //var arrow = this.el.select('.arrow',true).first();
20284         //arrow.set(align[2], 
20285         
20286         this.el.addClass('in');
20287         
20288          
20289         
20290         this.hoverState = 'in';
20291         
20292         if (this.modal) {
20293             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20294             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20295             this.maskEl.dom.style.display = 'block';
20296             this.maskEl.addClass('show');
20297         }
20298         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20299  
20300         this.fireEvent('show', this);
20301         
20302     },
20303     /**
20304      * fire this manually after loading a grid in the table for example
20305      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20306      * @param {Boolean} try and move it if we cant get right position.
20307      */
20308     updatePosition : function(placement, try_move)
20309     {
20310         // allow for calling with no parameters
20311         placement = placement   ? placement :  this.placement;
20312         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20313         
20314         this.el.removeClass([
20315             'fade','top','bottom', 'left', 'right','in',
20316             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20317         ]);
20318         this.el.addClass(placement + ' bs-popover-' + placement);
20319         
20320         if (!this.alignEl ) {
20321             return false;
20322         }
20323         
20324         switch (placement) {
20325             case 'right':
20326                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20327                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20328                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20329                     //normal display... or moved up/down.
20330                     this.el.setXY(offset);
20331                     var xy = this.alignEl.getAnchorXY('tr', false);
20332                     xy[0]+=2;xy[1]+=5;
20333                     this.arrowEl.setXY(xy);
20334                     return true;
20335                 }
20336                 // continue through...
20337                 return this.updatePosition('left', false);
20338                 
20339             
20340             case 'left':
20341                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20342                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20343                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20344                     //normal display... or moved up/down.
20345                     this.el.setXY(offset);
20346                     var xy = this.alignEl.getAnchorXY('tl', false);
20347                     xy[0]-=10;xy[1]+=5; // << fix me
20348                     this.arrowEl.setXY(xy);
20349                     return true;
20350                 }
20351                 // call self...
20352                 return this.updatePosition('right', false);
20353             
20354             case 'top':
20355                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20356                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20357                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20358                     //normal display... or moved up/down.
20359                     this.el.setXY(offset);
20360                     var xy = this.alignEl.getAnchorXY('t', false);
20361                     xy[1]-=10; // << fix me
20362                     this.arrowEl.setXY(xy);
20363                     return true;
20364                 }
20365                 // fall through
20366                return this.updatePosition('bottom', false);
20367             
20368             case 'bottom':
20369                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20370                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20371                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20372                     //normal display... or moved up/down.
20373                     this.el.setXY(offset);
20374                     var xy = this.alignEl.getAnchorXY('b', false);
20375                      xy[1]+=2; // << fix me
20376                     this.arrowEl.setXY(xy);
20377                     return true;
20378                 }
20379                 // fall through
20380                 return this.updatePosition('top', false);
20381                 
20382             
20383         }
20384         
20385         
20386         return false;
20387     },
20388     
20389     hide : function()
20390     {
20391         this.el.setXY([0,0]);
20392         this.el.removeClass('in');
20393         this.el.hide();
20394         this.hoverState = null;
20395         this.maskEl.hide(); // always..
20396         this.fireEvent('hide', this);
20397     }
20398     
20399 });
20400
20401
20402 Roo.apply(Roo.bootstrap.Popover, {
20403
20404     alignment : {
20405         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20406         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20407         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20408         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20409     },
20410     
20411     zIndex : 20001,
20412
20413     clickHander : false,
20414     
20415     
20416
20417     onMouseDown : function(e)
20418     {
20419         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20420             /// what is nothing is showing..
20421             this.hideAll();
20422         }
20423          
20424     },
20425     
20426     
20427     popups : [],
20428     
20429     register : function(popup)
20430     {
20431         if (!Roo.bootstrap.Popover.clickHandler) {
20432             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20433         }
20434         // hide other popups.
20435         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20436         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20437         this.hideAll(); //<< why?
20438         //this.popups.push(popup);
20439     },
20440     hideAll : function()
20441     {
20442         this.popups.forEach(function(p) {
20443             p.hide();
20444         });
20445     },
20446     onShow : function() {
20447         Roo.bootstrap.Popover.popups.push(this);
20448     },
20449     onHide : function() {
20450         Roo.bootstrap.Popover.popups.remove(this);
20451     } 
20452
20453 });/*
20454  * - LGPL
20455  *
20456  * Card header - holder for the card header elements.
20457  * 
20458  */
20459
20460 /**
20461  * @class Roo.bootstrap.PopoverNav
20462  * @extends Roo.bootstrap.NavGroup
20463  * Bootstrap Popover header navigation class
20464  * @constructor
20465  * Create a new Popover Header Navigation 
20466  * @param {Object} config The config object
20467  */
20468
20469 Roo.bootstrap.PopoverNav = function(config){
20470     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20471 };
20472
20473 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20474     
20475     
20476     container_method : 'getPopoverHeader' 
20477     
20478      
20479     
20480     
20481    
20482 });
20483
20484  
20485
20486  /*
20487  * - LGPL
20488  *
20489  * Progress
20490  * 
20491  */
20492
20493 /**
20494  * @class Roo.bootstrap.Progress
20495  * @extends Roo.bootstrap.Component
20496  * Bootstrap Progress class
20497  * @cfg {Boolean} striped striped of the progress bar
20498  * @cfg {Boolean} active animated of the progress bar
20499  * 
20500  * 
20501  * @constructor
20502  * Create a new Progress
20503  * @param {Object} config The config object
20504  */
20505
20506 Roo.bootstrap.Progress = function(config){
20507     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20508 };
20509
20510 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20511     
20512     striped : false,
20513     active: false,
20514     
20515     getAutoCreate : function(){
20516         var cfg = {
20517             tag: 'div',
20518             cls: 'progress'
20519         };
20520         
20521         
20522         if(this.striped){
20523             cfg.cls += ' progress-striped';
20524         }
20525       
20526         if(this.active){
20527             cfg.cls += ' active';
20528         }
20529         
20530         
20531         return cfg;
20532     }
20533    
20534 });
20535
20536  
20537
20538  /*
20539  * - LGPL
20540  *
20541  * ProgressBar
20542  * 
20543  */
20544
20545 /**
20546  * @class Roo.bootstrap.ProgressBar
20547  * @extends Roo.bootstrap.Component
20548  * Bootstrap ProgressBar class
20549  * @cfg {Number} aria_valuenow aria-value now
20550  * @cfg {Number} aria_valuemin aria-value min
20551  * @cfg {Number} aria_valuemax aria-value max
20552  * @cfg {String} label label for the progress bar
20553  * @cfg {String} panel (success | info | warning | danger )
20554  * @cfg {String} role role of the progress bar
20555  * @cfg {String} sr_only text
20556  * 
20557  * 
20558  * @constructor
20559  * Create a new ProgressBar
20560  * @param {Object} config The config object
20561  */
20562
20563 Roo.bootstrap.ProgressBar = function(config){
20564     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20565 };
20566
20567 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20568     
20569     aria_valuenow : 0,
20570     aria_valuemin : 0,
20571     aria_valuemax : 100,
20572     label : false,
20573     panel : false,
20574     role : false,
20575     sr_only: false,
20576     
20577     getAutoCreate : function()
20578     {
20579         
20580         var cfg = {
20581             tag: 'div',
20582             cls: 'progress-bar',
20583             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20584         };
20585         
20586         if(this.sr_only){
20587             cfg.cn = {
20588                 tag: 'span',
20589                 cls: 'sr-only',
20590                 html: this.sr_only
20591             }
20592         }
20593         
20594         if(this.role){
20595             cfg.role = this.role;
20596         }
20597         
20598         if(this.aria_valuenow){
20599             cfg['aria-valuenow'] = this.aria_valuenow;
20600         }
20601         
20602         if(this.aria_valuemin){
20603             cfg['aria-valuemin'] = this.aria_valuemin;
20604         }
20605         
20606         if(this.aria_valuemax){
20607             cfg['aria-valuemax'] = this.aria_valuemax;
20608         }
20609         
20610         if(this.label && !this.sr_only){
20611             cfg.html = this.label;
20612         }
20613         
20614         if(this.panel){
20615             cfg.cls += ' progress-bar-' + this.panel;
20616         }
20617         
20618         return cfg;
20619     },
20620     
20621     update : function(aria_valuenow)
20622     {
20623         this.aria_valuenow = aria_valuenow;
20624         
20625         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20626     }
20627    
20628 });
20629
20630  
20631
20632  /*
20633  * - LGPL
20634  *
20635  * column
20636  * 
20637  */
20638
20639 /**
20640  * @class Roo.bootstrap.TabGroup
20641  * @extends Roo.bootstrap.Column
20642  * Bootstrap Column class
20643  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20644  * @cfg {Boolean} carousel true to make the group behave like a carousel
20645  * @cfg {Boolean} bullets show bullets for the panels
20646  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20647  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20648  * @cfg {Boolean} showarrow (true|false) show arrow default true
20649  * 
20650  * @constructor
20651  * Create a new TabGroup
20652  * @param {Object} config The config object
20653  */
20654
20655 Roo.bootstrap.TabGroup = function(config){
20656     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20657     if (!this.navId) {
20658         this.navId = Roo.id();
20659     }
20660     this.tabs = [];
20661     Roo.bootstrap.TabGroup.register(this);
20662     
20663 };
20664
20665 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20666     
20667     carousel : false,
20668     transition : false,
20669     bullets : 0,
20670     timer : 0,
20671     autoslide : false,
20672     slideFn : false,
20673     slideOnTouch : false,
20674     showarrow : true,
20675     
20676     getAutoCreate : function()
20677     {
20678         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20679         
20680         cfg.cls += ' tab-content';
20681         
20682         if (this.carousel) {
20683             cfg.cls += ' carousel slide';
20684             
20685             cfg.cn = [{
20686                cls : 'carousel-inner',
20687                cn : []
20688             }];
20689         
20690             if(this.bullets  && !Roo.isTouch){
20691                 
20692                 var bullets = {
20693                     cls : 'carousel-bullets',
20694                     cn : []
20695                 };
20696                
20697                 if(this.bullets_cls){
20698                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20699                 }
20700                 
20701                 bullets.cn.push({
20702                     cls : 'clear'
20703                 });
20704                 
20705                 cfg.cn[0].cn.push(bullets);
20706             }
20707             
20708             if(this.showarrow){
20709                 cfg.cn[0].cn.push({
20710                     tag : 'div',
20711                     class : 'carousel-arrow',
20712                     cn : [
20713                         {
20714                             tag : 'div',
20715                             class : 'carousel-prev',
20716                             cn : [
20717                                 {
20718                                     tag : 'i',
20719                                     class : 'fa fa-chevron-left'
20720                                 }
20721                             ]
20722                         },
20723                         {
20724                             tag : 'div',
20725                             class : 'carousel-next',
20726                             cn : [
20727                                 {
20728                                     tag : 'i',
20729                                     class : 'fa fa-chevron-right'
20730                                 }
20731                             ]
20732                         }
20733                     ]
20734                 });
20735             }
20736             
20737         }
20738         
20739         return cfg;
20740     },
20741     
20742     initEvents:  function()
20743     {
20744 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20745 //            this.el.on("touchstart", this.onTouchStart, this);
20746 //        }
20747         
20748         if(this.autoslide){
20749             var _this = this;
20750             
20751             this.slideFn = window.setInterval(function() {
20752                 _this.showPanelNext();
20753             }, this.timer);
20754         }
20755         
20756         if(this.showarrow){
20757             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20758             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20759         }
20760         
20761         
20762     },
20763     
20764 //    onTouchStart : function(e, el, o)
20765 //    {
20766 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20767 //            return;
20768 //        }
20769 //        
20770 //        this.showPanelNext();
20771 //    },
20772     
20773     
20774     getChildContainer : function()
20775     {
20776         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20777     },
20778     
20779     /**
20780     * register a Navigation item
20781     * @param {Roo.bootstrap.NavItem} the navitem to add
20782     */
20783     register : function(item)
20784     {
20785         this.tabs.push( item);
20786         item.navId = this.navId; // not really needed..
20787         this.addBullet();
20788     
20789     },
20790     
20791     getActivePanel : function()
20792     {
20793         var r = false;
20794         Roo.each(this.tabs, function(t) {
20795             if (t.active) {
20796                 r = t;
20797                 return false;
20798             }
20799             return null;
20800         });
20801         return r;
20802         
20803     },
20804     getPanelByName : function(n)
20805     {
20806         var r = false;
20807         Roo.each(this.tabs, function(t) {
20808             if (t.tabId == n) {
20809                 r = t;
20810                 return false;
20811             }
20812             return null;
20813         });
20814         return r;
20815     },
20816     indexOfPanel : function(p)
20817     {
20818         var r = false;
20819         Roo.each(this.tabs, function(t,i) {
20820             if (t.tabId == p.tabId) {
20821                 r = i;
20822                 return false;
20823             }
20824             return null;
20825         });
20826         return r;
20827     },
20828     /**
20829      * show a specific panel
20830      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20831      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20832      */
20833     showPanel : function (pan)
20834     {
20835         if(this.transition || typeof(pan) == 'undefined'){
20836             Roo.log("waiting for the transitionend");
20837             return false;
20838         }
20839         
20840         if (typeof(pan) == 'number') {
20841             pan = this.tabs[pan];
20842         }
20843         
20844         if (typeof(pan) == 'string') {
20845             pan = this.getPanelByName(pan);
20846         }
20847         
20848         var cur = this.getActivePanel();
20849         
20850         if(!pan || !cur){
20851             Roo.log('pan or acitve pan is undefined');
20852             return false;
20853         }
20854         
20855         if (pan.tabId == this.getActivePanel().tabId) {
20856             return true;
20857         }
20858         
20859         if (false === cur.fireEvent('beforedeactivate')) {
20860             return false;
20861         }
20862         
20863         if(this.bullets > 0 && !Roo.isTouch){
20864             this.setActiveBullet(this.indexOfPanel(pan));
20865         }
20866         
20867         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20868             
20869             //class="carousel-item carousel-item-next carousel-item-left"
20870             
20871             this.transition = true;
20872             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20873             var lr = dir == 'next' ? 'left' : 'right';
20874             pan.el.addClass(dir); // or prev
20875             pan.el.addClass('carousel-item-' + dir); // or prev
20876             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20877             cur.el.addClass(lr); // or right
20878             pan.el.addClass(lr);
20879             cur.el.addClass('carousel-item-' +lr); // or right
20880             pan.el.addClass('carousel-item-' +lr);
20881             
20882             
20883             var _this = this;
20884             cur.el.on('transitionend', function() {
20885                 Roo.log("trans end?");
20886                 
20887                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20888                 pan.setActive(true);
20889                 
20890                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20891                 cur.setActive(false);
20892                 
20893                 _this.transition = false;
20894                 
20895             }, this, { single:  true } );
20896             
20897             return true;
20898         }
20899         
20900         cur.setActive(false);
20901         pan.setActive(true);
20902         
20903         return true;
20904         
20905     },
20906     showPanelNext : function()
20907     {
20908         var i = this.indexOfPanel(this.getActivePanel());
20909         
20910         if (i >= this.tabs.length - 1 && !this.autoslide) {
20911             return;
20912         }
20913         
20914         if (i >= this.tabs.length - 1 && this.autoslide) {
20915             i = -1;
20916         }
20917         
20918         this.showPanel(this.tabs[i+1]);
20919     },
20920     
20921     showPanelPrev : function()
20922     {
20923         var i = this.indexOfPanel(this.getActivePanel());
20924         
20925         if (i  < 1 && !this.autoslide) {
20926             return;
20927         }
20928         
20929         if (i < 1 && this.autoslide) {
20930             i = this.tabs.length;
20931         }
20932         
20933         this.showPanel(this.tabs[i-1]);
20934     },
20935     
20936     
20937     addBullet: function()
20938     {
20939         if(!this.bullets || Roo.isTouch){
20940             return;
20941         }
20942         var ctr = this.el.select('.carousel-bullets',true).first();
20943         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20944         var bullet = ctr.createChild({
20945             cls : 'bullet bullet-' + i
20946         },ctr.dom.lastChild);
20947         
20948         
20949         var _this = this;
20950         
20951         bullet.on('click', (function(e, el, o, ii, t){
20952
20953             e.preventDefault();
20954
20955             this.showPanel(ii);
20956
20957             if(this.autoslide && this.slideFn){
20958                 clearInterval(this.slideFn);
20959                 this.slideFn = window.setInterval(function() {
20960                     _this.showPanelNext();
20961                 }, this.timer);
20962             }
20963
20964         }).createDelegate(this, [i, bullet], true));
20965                 
20966         
20967     },
20968      
20969     setActiveBullet : function(i)
20970     {
20971         if(Roo.isTouch){
20972             return;
20973         }
20974         
20975         Roo.each(this.el.select('.bullet', true).elements, function(el){
20976             el.removeClass('selected');
20977         });
20978
20979         var bullet = this.el.select('.bullet-' + i, true).first();
20980         
20981         if(!bullet){
20982             return;
20983         }
20984         
20985         bullet.addClass('selected');
20986     }
20987     
20988     
20989   
20990 });
20991
20992  
20993
20994  
20995  
20996 Roo.apply(Roo.bootstrap.TabGroup, {
20997     
20998     groups: {},
20999      /**
21000     * register a Navigation Group
21001     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21002     */
21003     register : function(navgrp)
21004     {
21005         this.groups[navgrp.navId] = navgrp;
21006         
21007     },
21008     /**
21009     * fetch a Navigation Group based on the navigation ID
21010     * if one does not exist , it will get created.
21011     * @param {string} the navgroup to add
21012     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21013     */
21014     get: function(navId) {
21015         if (typeof(this.groups[navId]) == 'undefined') {
21016             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21017         }
21018         return this.groups[navId] ;
21019     }
21020     
21021     
21022     
21023 });
21024
21025  /*
21026  * - LGPL
21027  *
21028  * TabPanel
21029  * 
21030  */
21031
21032 /**
21033  * @class Roo.bootstrap.TabPanel
21034  * @extends Roo.bootstrap.Component
21035  * Bootstrap TabPanel class
21036  * @cfg {Boolean} active panel active
21037  * @cfg {String} html panel content
21038  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21039  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21040  * @cfg {String} href click to link..
21041  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21042  * 
21043  * 
21044  * @constructor
21045  * Create a new TabPanel
21046  * @param {Object} config The config object
21047  */
21048
21049 Roo.bootstrap.TabPanel = function(config){
21050     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21051     this.addEvents({
21052         /**
21053              * @event changed
21054              * Fires when the active status changes
21055              * @param {Roo.bootstrap.TabPanel} this
21056              * @param {Boolean} state the new state
21057             
21058          */
21059         'changed': true,
21060         /**
21061              * @event beforedeactivate
21062              * Fires before a tab is de-activated - can be used to do validation on a form.
21063              * @param {Roo.bootstrap.TabPanel} this
21064              * @return {Boolean} false if there is an error
21065             
21066          */
21067         'beforedeactivate': true
21068      });
21069     
21070     this.tabId = this.tabId || Roo.id();
21071   
21072 };
21073
21074 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21075     
21076     active: false,
21077     html: false,
21078     tabId: false,
21079     navId : false,
21080     href : '',
21081     touchSlide : false,
21082     getAutoCreate : function(){
21083         
21084         
21085         var cfg = {
21086             tag: 'div',
21087             // item is needed for carousel - not sure if it has any effect otherwise
21088             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21089             html: this.html || ''
21090         };
21091         
21092         if(this.active){
21093             cfg.cls += ' active';
21094         }
21095         
21096         if(this.tabId){
21097             cfg.tabId = this.tabId;
21098         }
21099         
21100         
21101         
21102         return cfg;
21103     },
21104     
21105     initEvents:  function()
21106     {
21107         var p = this.parent();
21108         
21109         this.navId = this.navId || p.navId;
21110         
21111         if (typeof(this.navId) != 'undefined') {
21112             // not really needed.. but just in case.. parent should be a NavGroup.
21113             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21114             
21115             tg.register(this);
21116             
21117             var i = tg.tabs.length - 1;
21118             
21119             if(this.active && tg.bullets > 0 && i < tg.bullets){
21120                 tg.setActiveBullet(i);
21121             }
21122         }
21123         
21124         this.el.on('click', this.onClick, this);
21125         
21126         if(Roo.isTouch && this.touchSlide){
21127             this.el.on("touchstart", this.onTouchStart, this);
21128             this.el.on("touchmove", this.onTouchMove, this);
21129             this.el.on("touchend", this.onTouchEnd, this);
21130         }
21131         
21132     },
21133     
21134     onRender : function(ct, position)
21135     {
21136         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21137     },
21138     
21139     setActive : function(state)
21140     {
21141         Roo.log("panel - set active " + this.tabId + "=" + state);
21142         
21143         this.active = state;
21144         if (!state) {
21145             this.el.removeClass('active');
21146             
21147         } else  if (!this.el.hasClass('active')) {
21148             this.el.addClass('active');
21149         }
21150         
21151         this.fireEvent('changed', this, state);
21152     },
21153     
21154     onClick : function(e)
21155     {
21156         e.preventDefault();
21157         
21158         if(!this.href.length){
21159             return;
21160         }
21161         
21162         window.location.href = this.href;
21163     },
21164     
21165     startX : 0,
21166     startY : 0,
21167     endX : 0,
21168     endY : 0,
21169     swiping : false,
21170     
21171     onTouchStart : function(e)
21172     {
21173         this.swiping = false;
21174         
21175         this.startX = e.browserEvent.touches[0].clientX;
21176         this.startY = e.browserEvent.touches[0].clientY;
21177     },
21178     
21179     onTouchMove : function(e)
21180     {
21181         this.swiping = true;
21182         
21183         this.endX = e.browserEvent.touches[0].clientX;
21184         this.endY = e.browserEvent.touches[0].clientY;
21185     },
21186     
21187     onTouchEnd : function(e)
21188     {
21189         if(!this.swiping){
21190             this.onClick(e);
21191             return;
21192         }
21193         
21194         var tabGroup = this.parent();
21195         
21196         if(this.endX > this.startX){ // swiping right
21197             tabGroup.showPanelPrev();
21198             return;
21199         }
21200         
21201         if(this.startX > this.endX){ // swiping left
21202             tabGroup.showPanelNext();
21203             return;
21204         }
21205     }
21206     
21207     
21208 });
21209  
21210
21211  
21212
21213  /*
21214  * - LGPL
21215  *
21216  * DateField
21217  * 
21218  */
21219
21220 /**
21221  * @class Roo.bootstrap.DateField
21222  * @extends Roo.bootstrap.Input
21223  * Bootstrap DateField class
21224  * @cfg {Number} weekStart default 0
21225  * @cfg {String} viewMode default empty, (months|years)
21226  * @cfg {String} minViewMode default empty, (months|years)
21227  * @cfg {Number} startDate default -Infinity
21228  * @cfg {Number} endDate default Infinity
21229  * @cfg {Boolean} todayHighlight default false
21230  * @cfg {Boolean} todayBtn default false
21231  * @cfg {Boolean} calendarWeeks default false
21232  * @cfg {Object} daysOfWeekDisabled default empty
21233  * @cfg {Boolean} singleMode default false (true | false)
21234  * 
21235  * @cfg {Boolean} keyboardNavigation default true
21236  * @cfg {String} language default en
21237  * 
21238  * @constructor
21239  * Create a new DateField
21240  * @param {Object} config The config object
21241  */
21242
21243 Roo.bootstrap.DateField = function(config){
21244     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21245      this.addEvents({
21246             /**
21247              * @event show
21248              * Fires when this field show.
21249              * @param {Roo.bootstrap.DateField} this
21250              * @param {Mixed} date The date value
21251              */
21252             show : true,
21253             /**
21254              * @event show
21255              * Fires when this field hide.
21256              * @param {Roo.bootstrap.DateField} this
21257              * @param {Mixed} date The date value
21258              */
21259             hide : true,
21260             /**
21261              * @event select
21262              * Fires when select a date.
21263              * @param {Roo.bootstrap.DateField} this
21264              * @param {Mixed} date The date value
21265              */
21266             select : true,
21267             /**
21268              * @event beforeselect
21269              * Fires when before select a date.
21270              * @param {Roo.bootstrap.DateField} this
21271              * @param {Mixed} date The date value
21272              */
21273             beforeselect : true
21274         });
21275 };
21276
21277 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21278     
21279     /**
21280      * @cfg {String} format
21281      * The default date format string which can be overriden for localization support.  The format must be
21282      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21283      */
21284     format : "m/d/y",
21285     /**
21286      * @cfg {String} altFormats
21287      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21288      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21289      */
21290     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21291     
21292     weekStart : 0,
21293     
21294     viewMode : '',
21295     
21296     minViewMode : '',
21297     
21298     todayHighlight : false,
21299     
21300     todayBtn: false,
21301     
21302     language: 'en',
21303     
21304     keyboardNavigation: true,
21305     
21306     calendarWeeks: false,
21307     
21308     startDate: -Infinity,
21309     
21310     endDate: Infinity,
21311     
21312     daysOfWeekDisabled: [],
21313     
21314     _events: [],
21315     
21316     singleMode : false,
21317     
21318     UTCDate: function()
21319     {
21320         return new Date(Date.UTC.apply(Date, arguments));
21321     },
21322     
21323     UTCToday: function()
21324     {
21325         var today = new Date();
21326         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21327     },
21328     
21329     getDate: function() {
21330             var d = this.getUTCDate();
21331             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21332     },
21333     
21334     getUTCDate: function() {
21335             return this.date;
21336     },
21337     
21338     setDate: function(d) {
21339             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21340     },
21341     
21342     setUTCDate: function(d) {
21343             this.date = d;
21344             this.setValue(this.formatDate(this.date));
21345     },
21346         
21347     onRender: function(ct, position)
21348     {
21349         
21350         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21351         
21352         this.language = this.language || 'en';
21353         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21354         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21355         
21356         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21357         this.format = this.format || 'm/d/y';
21358         this.isInline = false;
21359         this.isInput = true;
21360         this.component = this.el.select('.add-on', true).first() || false;
21361         this.component = (this.component && this.component.length === 0) ? false : this.component;
21362         this.hasInput = this.component && this.inputEl().length;
21363         
21364         if (typeof(this.minViewMode === 'string')) {
21365             switch (this.minViewMode) {
21366                 case 'months':
21367                     this.minViewMode = 1;
21368                     break;
21369                 case 'years':
21370                     this.minViewMode = 2;
21371                     break;
21372                 default:
21373                     this.minViewMode = 0;
21374                     break;
21375             }
21376         }
21377         
21378         if (typeof(this.viewMode === 'string')) {
21379             switch (this.viewMode) {
21380                 case 'months':
21381                     this.viewMode = 1;
21382                     break;
21383                 case 'years':
21384                     this.viewMode = 2;
21385                     break;
21386                 default:
21387                     this.viewMode = 0;
21388                     break;
21389             }
21390         }
21391                 
21392         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21393         
21394 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21395         
21396         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21397         
21398         this.picker().on('mousedown', this.onMousedown, this);
21399         this.picker().on('click', this.onClick, this);
21400         
21401         this.picker().addClass('datepicker-dropdown');
21402         
21403         this.startViewMode = this.viewMode;
21404         
21405         if(this.singleMode){
21406             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21407                 v.setVisibilityMode(Roo.Element.DISPLAY);
21408                 v.hide();
21409             });
21410             
21411             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21412                 v.setStyle('width', '189px');
21413             });
21414         }
21415         
21416         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21417             if(!this.calendarWeeks){
21418                 v.remove();
21419                 return;
21420             }
21421             
21422             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21423             v.attr('colspan', function(i, val){
21424                 return parseInt(val) + 1;
21425             });
21426         });
21427                         
21428         
21429         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21430         
21431         this.setStartDate(this.startDate);
21432         this.setEndDate(this.endDate);
21433         
21434         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21435         
21436         this.fillDow();
21437         this.fillMonths();
21438         this.update();
21439         this.showMode();
21440         
21441         if(this.isInline) {
21442             this.showPopup();
21443         }
21444     },
21445     
21446     picker : function()
21447     {
21448         return this.pickerEl;
21449 //        return this.el.select('.datepicker', true).first();
21450     },
21451     
21452     fillDow: function()
21453     {
21454         var dowCnt = this.weekStart;
21455         
21456         var dow = {
21457             tag: 'tr',
21458             cn: [
21459                 
21460             ]
21461         };
21462         
21463         if(this.calendarWeeks){
21464             dow.cn.push({
21465                 tag: 'th',
21466                 cls: 'cw',
21467                 html: '&nbsp;'
21468             })
21469         }
21470         
21471         while (dowCnt < this.weekStart + 7) {
21472             dow.cn.push({
21473                 tag: 'th',
21474                 cls: 'dow',
21475                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21476             });
21477         }
21478         
21479         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21480     },
21481     
21482     fillMonths: function()
21483     {    
21484         var i = 0;
21485         var months = this.picker().select('>.datepicker-months td', true).first();
21486         
21487         months.dom.innerHTML = '';
21488         
21489         while (i < 12) {
21490             var month = {
21491                 tag: 'span',
21492                 cls: 'month',
21493                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21494             };
21495             
21496             months.createChild(month);
21497         }
21498         
21499     },
21500     
21501     update: function()
21502     {
21503         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;
21504         
21505         if (this.date < this.startDate) {
21506             this.viewDate = new Date(this.startDate);
21507         } else if (this.date > this.endDate) {
21508             this.viewDate = new Date(this.endDate);
21509         } else {
21510             this.viewDate = new Date(this.date);
21511         }
21512         
21513         this.fill();
21514     },
21515     
21516     fill: function() 
21517     {
21518         var d = new Date(this.viewDate),
21519                 year = d.getUTCFullYear(),
21520                 month = d.getUTCMonth(),
21521                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21522                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21523                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21524                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21525                 currentDate = this.date && this.date.valueOf(),
21526                 today = this.UTCToday();
21527         
21528         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21529         
21530 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21531         
21532 //        this.picker.select('>tfoot th.today').
21533 //                                              .text(dates[this.language].today)
21534 //                                              .toggle(this.todayBtn !== false);
21535     
21536         this.updateNavArrows();
21537         this.fillMonths();
21538                                                 
21539         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21540         
21541         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21542          
21543         prevMonth.setUTCDate(day);
21544         
21545         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21546         
21547         var nextMonth = new Date(prevMonth);
21548         
21549         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21550         
21551         nextMonth = nextMonth.valueOf();
21552         
21553         var fillMonths = false;
21554         
21555         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21556         
21557         while(prevMonth.valueOf() <= nextMonth) {
21558             var clsName = '';
21559             
21560             if (prevMonth.getUTCDay() === this.weekStart) {
21561                 if(fillMonths){
21562                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21563                 }
21564                     
21565                 fillMonths = {
21566                     tag: 'tr',
21567                     cn: []
21568                 };
21569                 
21570                 if(this.calendarWeeks){
21571                     // ISO 8601: First week contains first thursday.
21572                     // ISO also states week starts on Monday, but we can be more abstract here.
21573                     var
21574                     // Start of current week: based on weekstart/current date
21575                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21576                     // Thursday of this week
21577                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21578                     // First Thursday of year, year from thursday
21579                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21580                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21581                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21582                     
21583                     fillMonths.cn.push({
21584                         tag: 'td',
21585                         cls: 'cw',
21586                         html: calWeek
21587                     });
21588                 }
21589             }
21590             
21591             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21592                 clsName += ' old';
21593             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21594                 clsName += ' new';
21595             }
21596             if (this.todayHighlight &&
21597                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21598                 prevMonth.getUTCMonth() == today.getMonth() &&
21599                 prevMonth.getUTCDate() == today.getDate()) {
21600                 clsName += ' today';
21601             }
21602             
21603             if (currentDate && prevMonth.valueOf() === currentDate) {
21604                 clsName += ' active';
21605             }
21606             
21607             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21608                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21609                     clsName += ' disabled';
21610             }
21611             
21612             fillMonths.cn.push({
21613                 tag: 'td',
21614                 cls: 'day ' + clsName,
21615                 html: prevMonth.getDate()
21616             });
21617             
21618             prevMonth.setDate(prevMonth.getDate()+1);
21619         }
21620           
21621         var currentYear = this.date && this.date.getUTCFullYear();
21622         var currentMonth = this.date && this.date.getUTCMonth();
21623         
21624         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21625         
21626         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21627             v.removeClass('active');
21628             
21629             if(currentYear === year && k === currentMonth){
21630                 v.addClass('active');
21631             }
21632             
21633             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21634                 v.addClass('disabled');
21635             }
21636             
21637         });
21638         
21639         
21640         year = parseInt(year/10, 10) * 10;
21641         
21642         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21643         
21644         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21645         
21646         year -= 1;
21647         for (var i = -1; i < 11; i++) {
21648             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21649                 tag: 'span',
21650                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21651                 html: year
21652             });
21653             
21654             year += 1;
21655         }
21656     },
21657     
21658     showMode: function(dir) 
21659     {
21660         if (dir) {
21661             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21662         }
21663         
21664         Roo.each(this.picker().select('>div',true).elements, function(v){
21665             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21666             v.hide();
21667         });
21668         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21669     },
21670     
21671     place: function()
21672     {
21673         if(this.isInline) {
21674             return;
21675         }
21676         
21677         this.picker().removeClass(['bottom', 'top']);
21678         
21679         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21680             /*
21681              * place to the top of element!
21682              *
21683              */
21684             
21685             this.picker().addClass('top');
21686             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21687             
21688             return;
21689         }
21690         
21691         this.picker().addClass('bottom');
21692         
21693         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21694     },
21695     
21696     parseDate : function(value)
21697     {
21698         if(!value || value instanceof Date){
21699             return value;
21700         }
21701         var v = Date.parseDate(value, this.format);
21702         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21703             v = Date.parseDate(value, 'Y-m-d');
21704         }
21705         if(!v && this.altFormats){
21706             if(!this.altFormatsArray){
21707                 this.altFormatsArray = this.altFormats.split("|");
21708             }
21709             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21710                 v = Date.parseDate(value, this.altFormatsArray[i]);
21711             }
21712         }
21713         return v;
21714     },
21715     
21716     formatDate : function(date, fmt)
21717     {   
21718         return (!date || !(date instanceof Date)) ?
21719         date : date.dateFormat(fmt || this.format);
21720     },
21721     
21722     onFocus : function()
21723     {
21724         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21725         this.showPopup();
21726     },
21727     
21728     onBlur : function()
21729     {
21730         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21731         
21732         var d = this.inputEl().getValue();
21733         
21734         this.setValue(d);
21735                 
21736         this.hidePopup();
21737     },
21738     
21739     showPopup : function()
21740     {
21741         this.picker().show();
21742         this.update();
21743         this.place();
21744         
21745         this.fireEvent('showpopup', this, this.date);
21746     },
21747     
21748     hidePopup : function()
21749     {
21750         if(this.isInline) {
21751             return;
21752         }
21753         this.picker().hide();
21754         this.viewMode = this.startViewMode;
21755         this.showMode();
21756         
21757         this.fireEvent('hidepopup', this, this.date);
21758         
21759     },
21760     
21761     onMousedown: function(e)
21762     {
21763         e.stopPropagation();
21764         e.preventDefault();
21765     },
21766     
21767     keyup: function(e)
21768     {
21769         Roo.bootstrap.DateField.superclass.keyup.call(this);
21770         this.update();
21771     },
21772
21773     setValue: function(v)
21774     {
21775         if(this.fireEvent('beforeselect', this, v) !== false){
21776             var d = new Date(this.parseDate(v) ).clearTime();
21777         
21778             if(isNaN(d.getTime())){
21779                 this.date = this.viewDate = '';
21780                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21781                 return;
21782             }
21783
21784             v = this.formatDate(d);
21785
21786             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21787
21788             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21789
21790             this.update();
21791
21792             this.fireEvent('select', this, this.date);
21793         }
21794     },
21795     
21796     getValue: function()
21797     {
21798         return this.formatDate(this.date);
21799     },
21800     
21801     fireKey: function(e)
21802     {
21803         if (!this.picker().isVisible()){
21804             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21805                 this.showPopup();
21806             }
21807             return;
21808         }
21809         
21810         var dateChanged = false,
21811         dir, day, month,
21812         newDate, newViewDate;
21813         
21814         switch(e.keyCode){
21815             case 27: // escape
21816                 this.hidePopup();
21817                 e.preventDefault();
21818                 break;
21819             case 37: // left
21820             case 39: // right
21821                 if (!this.keyboardNavigation) {
21822                     break;
21823                 }
21824                 dir = e.keyCode == 37 ? -1 : 1;
21825                 
21826                 if (e.ctrlKey){
21827                     newDate = this.moveYear(this.date, dir);
21828                     newViewDate = this.moveYear(this.viewDate, dir);
21829                 } else if (e.shiftKey){
21830                     newDate = this.moveMonth(this.date, dir);
21831                     newViewDate = this.moveMonth(this.viewDate, dir);
21832                 } else {
21833                     newDate = new Date(this.date);
21834                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21835                     newViewDate = new Date(this.viewDate);
21836                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21837                 }
21838                 if (this.dateWithinRange(newDate)){
21839                     this.date = newDate;
21840                     this.viewDate = newViewDate;
21841                     this.setValue(this.formatDate(this.date));
21842 //                    this.update();
21843                     e.preventDefault();
21844                     dateChanged = true;
21845                 }
21846                 break;
21847             case 38: // up
21848             case 40: // down
21849                 if (!this.keyboardNavigation) {
21850                     break;
21851                 }
21852                 dir = e.keyCode == 38 ? -1 : 1;
21853                 if (e.ctrlKey){
21854                     newDate = this.moveYear(this.date, dir);
21855                     newViewDate = this.moveYear(this.viewDate, dir);
21856                 } else if (e.shiftKey){
21857                     newDate = this.moveMonth(this.date, dir);
21858                     newViewDate = this.moveMonth(this.viewDate, dir);
21859                 } else {
21860                     newDate = new Date(this.date);
21861                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21862                     newViewDate = new Date(this.viewDate);
21863                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21864                 }
21865                 if (this.dateWithinRange(newDate)){
21866                     this.date = newDate;
21867                     this.viewDate = newViewDate;
21868                     this.setValue(this.formatDate(this.date));
21869 //                    this.update();
21870                     e.preventDefault();
21871                     dateChanged = true;
21872                 }
21873                 break;
21874             case 13: // enter
21875                 this.setValue(this.formatDate(this.date));
21876                 this.hidePopup();
21877                 e.preventDefault();
21878                 break;
21879             case 9: // tab
21880                 this.setValue(this.formatDate(this.date));
21881                 this.hidePopup();
21882                 break;
21883             case 16: // shift
21884             case 17: // ctrl
21885             case 18: // alt
21886                 break;
21887             default :
21888                 this.hidePopup();
21889                 
21890         }
21891     },
21892     
21893     
21894     onClick: function(e) 
21895     {
21896         e.stopPropagation();
21897         e.preventDefault();
21898         
21899         var target = e.getTarget();
21900         
21901         if(target.nodeName.toLowerCase() === 'i'){
21902             target = Roo.get(target).dom.parentNode;
21903         }
21904         
21905         var nodeName = target.nodeName;
21906         var className = target.className;
21907         var html = target.innerHTML;
21908         //Roo.log(nodeName);
21909         
21910         switch(nodeName.toLowerCase()) {
21911             case 'th':
21912                 switch(className) {
21913                     case 'switch':
21914                         this.showMode(1);
21915                         break;
21916                     case 'prev':
21917                     case 'next':
21918                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21919                         switch(this.viewMode){
21920                                 case 0:
21921                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21922                                         break;
21923                                 case 1:
21924                                 case 2:
21925                                         this.viewDate = this.moveYear(this.viewDate, dir);
21926                                         break;
21927                         }
21928                         this.fill();
21929                         break;
21930                     case 'today':
21931                         var date = new Date();
21932                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21933 //                        this.fill()
21934                         this.setValue(this.formatDate(this.date));
21935                         
21936                         this.hidePopup();
21937                         break;
21938                 }
21939                 break;
21940             case 'span':
21941                 if (className.indexOf('disabled') < 0) {
21942                     this.viewDate.setUTCDate(1);
21943                     if (className.indexOf('month') > -1) {
21944                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21945                     } else {
21946                         var year = parseInt(html, 10) || 0;
21947                         this.viewDate.setUTCFullYear(year);
21948                         
21949                     }
21950                     
21951                     if(this.singleMode){
21952                         this.setValue(this.formatDate(this.viewDate));
21953                         this.hidePopup();
21954                         return;
21955                     }
21956                     
21957                     this.showMode(-1);
21958                     this.fill();
21959                 }
21960                 break;
21961                 
21962             case 'td':
21963                 //Roo.log(className);
21964                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21965                     var day = parseInt(html, 10) || 1;
21966                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21967                         month = (this.viewDate || new Date()).getUTCMonth();
21968
21969                     if (className.indexOf('old') > -1) {
21970                         if(month === 0 ){
21971                             month = 11;
21972                             year -= 1;
21973                         }else{
21974                             month -= 1;
21975                         }
21976                     } else if (className.indexOf('new') > -1) {
21977                         if (month == 11) {
21978                             month = 0;
21979                             year += 1;
21980                         } else {
21981                             month += 1;
21982                         }
21983                     }
21984                     //Roo.log([year,month,day]);
21985                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21986                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21987 //                    this.fill();
21988                     //Roo.log(this.formatDate(this.date));
21989                     this.setValue(this.formatDate(this.date));
21990                     this.hidePopup();
21991                 }
21992                 break;
21993         }
21994     },
21995     
21996     setStartDate: function(startDate)
21997     {
21998         this.startDate = startDate || -Infinity;
21999         if (this.startDate !== -Infinity) {
22000             this.startDate = this.parseDate(this.startDate);
22001         }
22002         this.update();
22003         this.updateNavArrows();
22004     },
22005
22006     setEndDate: function(endDate)
22007     {
22008         this.endDate = endDate || Infinity;
22009         if (this.endDate !== Infinity) {
22010             this.endDate = this.parseDate(this.endDate);
22011         }
22012         this.update();
22013         this.updateNavArrows();
22014     },
22015     
22016     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22017     {
22018         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22019         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22020             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22021         }
22022         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22023             return parseInt(d, 10);
22024         });
22025         this.update();
22026         this.updateNavArrows();
22027     },
22028     
22029     updateNavArrows: function() 
22030     {
22031         if(this.singleMode){
22032             return;
22033         }
22034         
22035         var d = new Date(this.viewDate),
22036         year = d.getUTCFullYear(),
22037         month = d.getUTCMonth();
22038         
22039         Roo.each(this.picker().select('.prev', true).elements, function(v){
22040             v.show();
22041             switch (this.viewMode) {
22042                 case 0:
22043
22044                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22045                         v.hide();
22046                     }
22047                     break;
22048                 case 1:
22049                 case 2:
22050                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22051                         v.hide();
22052                     }
22053                     break;
22054             }
22055         });
22056         
22057         Roo.each(this.picker().select('.next', true).elements, function(v){
22058             v.show();
22059             switch (this.viewMode) {
22060                 case 0:
22061
22062                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22063                         v.hide();
22064                     }
22065                     break;
22066                 case 1:
22067                 case 2:
22068                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22069                         v.hide();
22070                     }
22071                     break;
22072             }
22073         })
22074     },
22075     
22076     moveMonth: function(date, dir)
22077     {
22078         if (!dir) {
22079             return date;
22080         }
22081         var new_date = new Date(date.valueOf()),
22082         day = new_date.getUTCDate(),
22083         month = new_date.getUTCMonth(),
22084         mag = Math.abs(dir),
22085         new_month, test;
22086         dir = dir > 0 ? 1 : -1;
22087         if (mag == 1){
22088             test = dir == -1
22089             // If going back one month, make sure month is not current month
22090             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22091             ? function(){
22092                 return new_date.getUTCMonth() == month;
22093             }
22094             // If going forward one month, make sure month is as expected
22095             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22096             : function(){
22097                 return new_date.getUTCMonth() != new_month;
22098             };
22099             new_month = month + dir;
22100             new_date.setUTCMonth(new_month);
22101             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22102             if (new_month < 0 || new_month > 11) {
22103                 new_month = (new_month + 12) % 12;
22104             }
22105         } else {
22106             // For magnitudes >1, move one month at a time...
22107             for (var i=0; i<mag; i++) {
22108                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22109                 new_date = this.moveMonth(new_date, dir);
22110             }
22111             // ...then reset the day, keeping it in the new month
22112             new_month = new_date.getUTCMonth();
22113             new_date.setUTCDate(day);
22114             test = function(){
22115                 return new_month != new_date.getUTCMonth();
22116             };
22117         }
22118         // Common date-resetting loop -- if date is beyond end of month, make it
22119         // end of month
22120         while (test()){
22121             new_date.setUTCDate(--day);
22122             new_date.setUTCMonth(new_month);
22123         }
22124         return new_date;
22125     },
22126
22127     moveYear: function(date, dir)
22128     {
22129         return this.moveMonth(date, dir*12);
22130     },
22131
22132     dateWithinRange: function(date)
22133     {
22134         return date >= this.startDate && date <= this.endDate;
22135     },
22136
22137     
22138     remove: function() 
22139     {
22140         this.picker().remove();
22141     },
22142     
22143     validateValue : function(value)
22144     {
22145         if(this.getVisibilityEl().hasClass('hidden')){
22146             return true;
22147         }
22148         
22149         if(value.length < 1)  {
22150             if(this.allowBlank){
22151                 return true;
22152             }
22153             return false;
22154         }
22155         
22156         if(value.length < this.minLength){
22157             return false;
22158         }
22159         if(value.length > this.maxLength){
22160             return false;
22161         }
22162         if(this.vtype){
22163             var vt = Roo.form.VTypes;
22164             if(!vt[this.vtype](value, this)){
22165                 return false;
22166             }
22167         }
22168         if(typeof this.validator == "function"){
22169             var msg = this.validator(value);
22170             if(msg !== true){
22171                 return false;
22172             }
22173         }
22174         
22175         if(this.regex && !this.regex.test(value)){
22176             return false;
22177         }
22178         
22179         if(typeof(this.parseDate(value)) == 'undefined'){
22180             return false;
22181         }
22182         
22183         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22184             return false;
22185         }      
22186         
22187         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22188             return false;
22189         } 
22190         
22191         
22192         return true;
22193     },
22194     
22195     reset : function()
22196     {
22197         this.date = this.viewDate = '';
22198         
22199         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22200     }
22201    
22202 });
22203
22204 Roo.apply(Roo.bootstrap.DateField,  {
22205     
22206     head : {
22207         tag: 'thead',
22208         cn: [
22209         {
22210             tag: 'tr',
22211             cn: [
22212             {
22213                 tag: 'th',
22214                 cls: 'prev',
22215                 html: '<i class="fa fa-arrow-left"/>'
22216             },
22217             {
22218                 tag: 'th',
22219                 cls: 'switch',
22220                 colspan: '5'
22221             },
22222             {
22223                 tag: 'th',
22224                 cls: 'next',
22225                 html: '<i class="fa fa-arrow-right"/>'
22226             }
22227
22228             ]
22229         }
22230         ]
22231     },
22232     
22233     content : {
22234         tag: 'tbody',
22235         cn: [
22236         {
22237             tag: 'tr',
22238             cn: [
22239             {
22240                 tag: 'td',
22241                 colspan: '7'
22242             }
22243             ]
22244         }
22245         ]
22246     },
22247     
22248     footer : {
22249         tag: 'tfoot',
22250         cn: [
22251         {
22252             tag: 'tr',
22253             cn: [
22254             {
22255                 tag: 'th',
22256                 colspan: '7',
22257                 cls: 'today'
22258             }
22259                     
22260             ]
22261         }
22262         ]
22263     },
22264     
22265     dates:{
22266         en: {
22267             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22268             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22269             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22270             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22271             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22272             today: "Today"
22273         }
22274     },
22275     
22276     modes: [
22277     {
22278         clsName: 'days',
22279         navFnc: 'Month',
22280         navStep: 1
22281     },
22282     {
22283         clsName: 'months',
22284         navFnc: 'FullYear',
22285         navStep: 1
22286     },
22287     {
22288         clsName: 'years',
22289         navFnc: 'FullYear',
22290         navStep: 10
22291     }]
22292 });
22293
22294 Roo.apply(Roo.bootstrap.DateField,  {
22295   
22296     template : {
22297         tag: 'div',
22298         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22299         cn: [
22300         {
22301             tag: 'div',
22302             cls: 'datepicker-days',
22303             cn: [
22304             {
22305                 tag: 'table',
22306                 cls: 'table-condensed',
22307                 cn:[
22308                 Roo.bootstrap.DateField.head,
22309                 {
22310                     tag: 'tbody'
22311                 },
22312                 Roo.bootstrap.DateField.footer
22313                 ]
22314             }
22315             ]
22316         },
22317         {
22318             tag: 'div',
22319             cls: 'datepicker-months',
22320             cn: [
22321             {
22322                 tag: 'table',
22323                 cls: 'table-condensed',
22324                 cn:[
22325                 Roo.bootstrap.DateField.head,
22326                 Roo.bootstrap.DateField.content,
22327                 Roo.bootstrap.DateField.footer
22328                 ]
22329             }
22330             ]
22331         },
22332         {
22333             tag: 'div',
22334             cls: 'datepicker-years',
22335             cn: [
22336             {
22337                 tag: 'table',
22338                 cls: 'table-condensed',
22339                 cn:[
22340                 Roo.bootstrap.DateField.head,
22341                 Roo.bootstrap.DateField.content,
22342                 Roo.bootstrap.DateField.footer
22343                 ]
22344             }
22345             ]
22346         }
22347         ]
22348     }
22349 });
22350
22351  
22352
22353  /*
22354  * - LGPL
22355  *
22356  * TimeField
22357  * 
22358  */
22359
22360 /**
22361  * @class Roo.bootstrap.TimeField
22362  * @extends Roo.bootstrap.Input
22363  * Bootstrap DateField class
22364  * 
22365  * 
22366  * @constructor
22367  * Create a new TimeField
22368  * @param {Object} config The config object
22369  */
22370
22371 Roo.bootstrap.TimeField = function(config){
22372     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22373     this.addEvents({
22374             /**
22375              * @event show
22376              * Fires when this field show.
22377              * @param {Roo.bootstrap.DateField} thisthis
22378              * @param {Mixed} date The date value
22379              */
22380             show : true,
22381             /**
22382              * @event show
22383              * Fires when this field hide.
22384              * @param {Roo.bootstrap.DateField} this
22385              * @param {Mixed} date The date value
22386              */
22387             hide : true,
22388             /**
22389              * @event select
22390              * Fires when select a date.
22391              * @param {Roo.bootstrap.DateField} this
22392              * @param {Mixed} date The date value
22393              */
22394             select : true
22395         });
22396 };
22397
22398 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22399     
22400     /**
22401      * @cfg {String} format
22402      * The default time format string which can be overriden for localization support.  The format must be
22403      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22404      */
22405     format : "H:i",
22406
22407     getAutoCreate : function()
22408     {
22409         this.after = '<i class="fa far fa-clock"></i>';
22410         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22411         
22412          
22413     },
22414     onRender: function(ct, position)
22415     {
22416         
22417         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22418                 
22419         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22420         
22421         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22422         
22423         this.pop = this.picker().select('>.datepicker-time',true).first();
22424         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22425         
22426         this.picker().on('mousedown', this.onMousedown, this);
22427         this.picker().on('click', this.onClick, this);
22428         
22429         this.picker().addClass('datepicker-dropdown');
22430     
22431         this.fillTime();
22432         this.update();
22433             
22434         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22435         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22436         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22437         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22438         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22439         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22440
22441     },
22442     
22443     fireKey: function(e){
22444         if (!this.picker().isVisible()){
22445             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22446                 this.show();
22447             }
22448             return;
22449         }
22450
22451         e.preventDefault();
22452         
22453         switch(e.keyCode){
22454             case 27: // escape
22455                 this.hide();
22456                 break;
22457             case 37: // left
22458             case 39: // right
22459                 this.onTogglePeriod();
22460                 break;
22461             case 38: // up
22462                 this.onIncrementMinutes();
22463                 break;
22464             case 40: // down
22465                 this.onDecrementMinutes();
22466                 break;
22467             case 13: // enter
22468             case 9: // tab
22469                 this.setTime();
22470                 break;
22471         }
22472     },
22473     
22474     onClick: function(e) {
22475         e.stopPropagation();
22476         e.preventDefault();
22477     },
22478     
22479     picker : function()
22480     {
22481         return this.pickerEl;
22482     },
22483     
22484     fillTime: function()
22485     {    
22486         var time = this.pop.select('tbody', true).first();
22487         
22488         time.dom.innerHTML = '';
22489         
22490         time.createChild({
22491             tag: 'tr',
22492             cn: [
22493                 {
22494                     tag: 'td',
22495                     cn: [
22496                         {
22497                             tag: 'a',
22498                             href: '#',
22499                             cls: 'btn',
22500                             cn: [
22501                                 {
22502                                     tag: 'i',
22503                                     cls: 'hours-up fa fas fa-chevron-up'
22504                                 }
22505                             ]
22506                         } 
22507                     ]
22508                 },
22509                 {
22510                     tag: 'td',
22511                     cls: 'separator'
22512                 },
22513                 {
22514                     tag: 'td',
22515                     cn: [
22516                         {
22517                             tag: 'a',
22518                             href: '#',
22519                             cls: 'btn',
22520                             cn: [
22521                                 {
22522                                     tag: 'i',
22523                                     cls: 'minutes-up fa fas fa-chevron-up'
22524                                 }
22525                             ]
22526                         }
22527                     ]
22528                 },
22529                 {
22530                     tag: 'td',
22531                     cls: 'separator'
22532                 }
22533             ]
22534         });
22535         
22536         time.createChild({
22537             tag: 'tr',
22538             cn: [
22539                 {
22540                     tag: 'td',
22541                     cn: [
22542                         {
22543                             tag: 'span',
22544                             cls: 'timepicker-hour',
22545                             html: '00'
22546                         }  
22547                     ]
22548                 },
22549                 {
22550                     tag: 'td',
22551                     cls: 'separator',
22552                     html: ':'
22553                 },
22554                 {
22555                     tag: 'td',
22556                     cn: [
22557                         {
22558                             tag: 'span',
22559                             cls: 'timepicker-minute',
22560                             html: '00'
22561                         }  
22562                     ]
22563                 },
22564                 {
22565                     tag: 'td',
22566                     cls: 'separator'
22567                 },
22568                 {
22569                     tag: 'td',
22570                     cn: [
22571                         {
22572                             tag: 'button',
22573                             type: 'button',
22574                             cls: 'btn btn-primary period',
22575                             html: 'AM'
22576                             
22577                         }
22578                     ]
22579                 }
22580             ]
22581         });
22582         
22583         time.createChild({
22584             tag: 'tr',
22585             cn: [
22586                 {
22587                     tag: 'td',
22588                     cn: [
22589                         {
22590                             tag: 'a',
22591                             href: '#',
22592                             cls: 'btn',
22593                             cn: [
22594                                 {
22595                                     tag: 'span',
22596                                     cls: 'hours-down fa fas fa-chevron-down'
22597                                 }
22598                             ]
22599                         }
22600                     ]
22601                 },
22602                 {
22603                     tag: 'td',
22604                     cls: 'separator'
22605                 },
22606                 {
22607                     tag: 'td',
22608                     cn: [
22609                         {
22610                             tag: 'a',
22611                             href: '#',
22612                             cls: 'btn',
22613                             cn: [
22614                                 {
22615                                     tag: 'span',
22616                                     cls: 'minutes-down fa fas fa-chevron-down'
22617                                 }
22618                             ]
22619                         }
22620                     ]
22621                 },
22622                 {
22623                     tag: 'td',
22624                     cls: 'separator'
22625                 }
22626             ]
22627         });
22628         
22629     },
22630     
22631     update: function()
22632     {
22633         
22634         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22635         
22636         this.fill();
22637     },
22638     
22639     fill: function() 
22640     {
22641         var hours = this.time.getHours();
22642         var minutes = this.time.getMinutes();
22643         var period = 'AM';
22644         
22645         if(hours > 11){
22646             period = 'PM';
22647         }
22648         
22649         if(hours == 0){
22650             hours = 12;
22651         }
22652         
22653         
22654         if(hours > 12){
22655             hours = hours - 12;
22656         }
22657         
22658         if(hours < 10){
22659             hours = '0' + hours;
22660         }
22661         
22662         if(minutes < 10){
22663             minutes = '0' + minutes;
22664         }
22665         
22666         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22667         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22668         this.pop.select('button', true).first().dom.innerHTML = period;
22669         
22670     },
22671     
22672     place: function()
22673     {   
22674         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22675         
22676         var cls = ['bottom'];
22677         
22678         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22679             cls.pop();
22680             cls.push('top');
22681         }
22682         
22683         cls.push('right');
22684         
22685         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22686             cls.pop();
22687             cls.push('left');
22688         }
22689         //this.picker().setXY(20000,20000);
22690         this.picker().addClass(cls.join('-'));
22691         
22692         var _this = this;
22693         
22694         Roo.each(cls, function(c){
22695             if(c == 'bottom'){
22696                 (function() {
22697                  //  
22698                 }).defer(200);
22699                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22700                 //_this.picker().setTop(_this.inputEl().getHeight());
22701                 return;
22702             }
22703             if(c == 'top'){
22704                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22705                 
22706                 //_this.picker().setTop(0 - _this.picker().getHeight());
22707                 return;
22708             }
22709             /*
22710             if(c == 'left'){
22711                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22712                 return;
22713             }
22714             if(c == 'right'){
22715                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22716                 return;
22717             }
22718             */
22719         });
22720         
22721     },
22722   
22723     onFocus : function()
22724     {
22725         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22726         this.show();
22727     },
22728     
22729     onBlur : function()
22730     {
22731         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22732         this.hide();
22733     },
22734     
22735     show : function()
22736     {
22737         this.picker().show();
22738         this.pop.show();
22739         this.update();
22740         this.place();
22741         
22742         this.fireEvent('show', this, this.date);
22743     },
22744     
22745     hide : function()
22746     {
22747         this.picker().hide();
22748         this.pop.hide();
22749         
22750         this.fireEvent('hide', this, this.date);
22751     },
22752     
22753     setTime : function()
22754     {
22755         this.hide();
22756         this.setValue(this.time.format(this.format));
22757         
22758         this.fireEvent('select', this, this.date);
22759         
22760         
22761     },
22762     
22763     onMousedown: function(e){
22764         e.stopPropagation();
22765         e.preventDefault();
22766     },
22767     
22768     onIncrementHours: function()
22769     {
22770         Roo.log('onIncrementHours');
22771         this.time = this.time.add(Date.HOUR, 1);
22772         this.update();
22773         
22774     },
22775     
22776     onDecrementHours: function()
22777     {
22778         Roo.log('onDecrementHours');
22779         this.time = this.time.add(Date.HOUR, -1);
22780         this.update();
22781     },
22782     
22783     onIncrementMinutes: function()
22784     {
22785         Roo.log('onIncrementMinutes');
22786         this.time = this.time.add(Date.MINUTE, 1);
22787         this.update();
22788     },
22789     
22790     onDecrementMinutes: function()
22791     {
22792         Roo.log('onDecrementMinutes');
22793         this.time = this.time.add(Date.MINUTE, -1);
22794         this.update();
22795     },
22796     
22797     onTogglePeriod: function()
22798     {
22799         Roo.log('onTogglePeriod');
22800         this.time = this.time.add(Date.HOUR, 12);
22801         this.update();
22802     }
22803     
22804    
22805 });
22806  
22807
22808 Roo.apply(Roo.bootstrap.TimeField,  {
22809   
22810     template : {
22811         tag: 'div',
22812         cls: 'datepicker dropdown-menu',
22813         cn: [
22814             {
22815                 tag: 'div',
22816                 cls: 'datepicker-time',
22817                 cn: [
22818                 {
22819                     tag: 'table',
22820                     cls: 'table-condensed',
22821                     cn:[
22822                         {
22823                             tag: 'tbody',
22824                             cn: [
22825                                 {
22826                                     tag: 'tr',
22827                                     cn: [
22828                                     {
22829                                         tag: 'td',
22830                                         colspan: '7'
22831                                     }
22832                                     ]
22833                                 }
22834                             ]
22835                         },
22836                         {
22837                             tag: 'tfoot',
22838                             cn: [
22839                                 {
22840                                     tag: 'tr',
22841                                     cn: [
22842                                     {
22843                                         tag: 'th',
22844                                         colspan: '7',
22845                                         cls: '',
22846                                         cn: [
22847                                             {
22848                                                 tag: 'button',
22849                                                 cls: 'btn btn-info ok',
22850                                                 html: 'OK'
22851                                             }
22852                                         ]
22853                                     }
22854                     
22855                                     ]
22856                                 }
22857                             ]
22858                         }
22859                     ]
22860                 }
22861                 ]
22862             }
22863         ]
22864     }
22865 });
22866
22867  
22868
22869  /*
22870  * - LGPL
22871  *
22872  * MonthField
22873  * 
22874  */
22875
22876 /**
22877  * @class Roo.bootstrap.MonthField
22878  * @extends Roo.bootstrap.Input
22879  * Bootstrap MonthField class
22880  * 
22881  * @cfg {String} language default en
22882  * 
22883  * @constructor
22884  * Create a new MonthField
22885  * @param {Object} config The config object
22886  */
22887
22888 Roo.bootstrap.MonthField = function(config){
22889     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22890     
22891     this.addEvents({
22892         /**
22893          * @event show
22894          * Fires when this field show.
22895          * @param {Roo.bootstrap.MonthField} this
22896          * @param {Mixed} date The date value
22897          */
22898         show : true,
22899         /**
22900          * @event show
22901          * Fires when this field hide.
22902          * @param {Roo.bootstrap.MonthField} this
22903          * @param {Mixed} date The date value
22904          */
22905         hide : true,
22906         /**
22907          * @event select
22908          * Fires when select a date.
22909          * @param {Roo.bootstrap.MonthField} this
22910          * @param {String} oldvalue The old value
22911          * @param {String} newvalue The new value
22912          */
22913         select : true
22914     });
22915 };
22916
22917 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22918     
22919     onRender: function(ct, position)
22920     {
22921         
22922         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22923         
22924         this.language = this.language || 'en';
22925         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22926         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22927         
22928         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22929         this.isInline = false;
22930         this.isInput = true;
22931         this.component = this.el.select('.add-on', true).first() || false;
22932         this.component = (this.component && this.component.length === 0) ? false : this.component;
22933         this.hasInput = this.component && this.inputEL().length;
22934         
22935         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22936         
22937         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22938         
22939         this.picker().on('mousedown', this.onMousedown, this);
22940         this.picker().on('click', this.onClick, this);
22941         
22942         this.picker().addClass('datepicker-dropdown');
22943         
22944         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22945             v.setStyle('width', '189px');
22946         });
22947         
22948         this.fillMonths();
22949         
22950         this.update();
22951         
22952         if(this.isInline) {
22953             this.show();
22954         }
22955         
22956     },
22957     
22958     setValue: function(v, suppressEvent)
22959     {   
22960         var o = this.getValue();
22961         
22962         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22963         
22964         this.update();
22965
22966         if(suppressEvent !== true){
22967             this.fireEvent('select', this, o, v);
22968         }
22969         
22970     },
22971     
22972     getValue: function()
22973     {
22974         return this.value;
22975     },
22976     
22977     onClick: function(e) 
22978     {
22979         e.stopPropagation();
22980         e.preventDefault();
22981         
22982         var target = e.getTarget();
22983         
22984         if(target.nodeName.toLowerCase() === 'i'){
22985             target = Roo.get(target).dom.parentNode;
22986         }
22987         
22988         var nodeName = target.nodeName;
22989         var className = target.className;
22990         var html = target.innerHTML;
22991         
22992         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22993             return;
22994         }
22995         
22996         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22997         
22998         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22999         
23000         this.hide();
23001                         
23002     },
23003     
23004     picker : function()
23005     {
23006         return this.pickerEl;
23007     },
23008     
23009     fillMonths: function()
23010     {    
23011         var i = 0;
23012         var months = this.picker().select('>.datepicker-months td', true).first();
23013         
23014         months.dom.innerHTML = '';
23015         
23016         while (i < 12) {
23017             var month = {
23018                 tag: 'span',
23019                 cls: 'month',
23020                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23021             };
23022             
23023             months.createChild(month);
23024         }
23025         
23026     },
23027     
23028     update: function()
23029     {
23030         var _this = this;
23031         
23032         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23033             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23034         }
23035         
23036         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23037             e.removeClass('active');
23038             
23039             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23040                 e.addClass('active');
23041             }
23042         })
23043     },
23044     
23045     place: function()
23046     {
23047         if(this.isInline) {
23048             return;
23049         }
23050         
23051         this.picker().removeClass(['bottom', 'top']);
23052         
23053         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23054             /*
23055              * place to the top of element!
23056              *
23057              */
23058             
23059             this.picker().addClass('top');
23060             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23061             
23062             return;
23063         }
23064         
23065         this.picker().addClass('bottom');
23066         
23067         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23068     },
23069     
23070     onFocus : function()
23071     {
23072         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23073         this.show();
23074     },
23075     
23076     onBlur : function()
23077     {
23078         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23079         
23080         var d = this.inputEl().getValue();
23081         
23082         this.setValue(d);
23083                 
23084         this.hide();
23085     },
23086     
23087     show : function()
23088     {
23089         this.picker().show();
23090         this.picker().select('>.datepicker-months', true).first().show();
23091         this.update();
23092         this.place();
23093         
23094         this.fireEvent('show', this, this.date);
23095     },
23096     
23097     hide : function()
23098     {
23099         if(this.isInline) {
23100             return;
23101         }
23102         this.picker().hide();
23103         this.fireEvent('hide', this, this.date);
23104         
23105     },
23106     
23107     onMousedown: function(e)
23108     {
23109         e.stopPropagation();
23110         e.preventDefault();
23111     },
23112     
23113     keyup: function(e)
23114     {
23115         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23116         this.update();
23117     },
23118
23119     fireKey: function(e)
23120     {
23121         if (!this.picker().isVisible()){
23122             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23123                 this.show();
23124             }
23125             return;
23126         }
23127         
23128         var dir;
23129         
23130         switch(e.keyCode){
23131             case 27: // escape
23132                 this.hide();
23133                 e.preventDefault();
23134                 break;
23135             case 37: // left
23136             case 39: // right
23137                 dir = e.keyCode == 37 ? -1 : 1;
23138                 
23139                 this.vIndex = this.vIndex + dir;
23140                 
23141                 if(this.vIndex < 0){
23142                     this.vIndex = 0;
23143                 }
23144                 
23145                 if(this.vIndex > 11){
23146                     this.vIndex = 11;
23147                 }
23148                 
23149                 if(isNaN(this.vIndex)){
23150                     this.vIndex = 0;
23151                 }
23152                 
23153                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23154                 
23155                 break;
23156             case 38: // up
23157             case 40: // down
23158                 
23159                 dir = e.keyCode == 38 ? -1 : 1;
23160                 
23161                 this.vIndex = this.vIndex + dir * 4;
23162                 
23163                 if(this.vIndex < 0){
23164                     this.vIndex = 0;
23165                 }
23166                 
23167                 if(this.vIndex > 11){
23168                     this.vIndex = 11;
23169                 }
23170                 
23171                 if(isNaN(this.vIndex)){
23172                     this.vIndex = 0;
23173                 }
23174                 
23175                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23176                 break;
23177                 
23178             case 13: // enter
23179                 
23180                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23181                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23182                 }
23183                 
23184                 this.hide();
23185                 e.preventDefault();
23186                 break;
23187             case 9: // tab
23188                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23189                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23190                 }
23191                 this.hide();
23192                 break;
23193             case 16: // shift
23194             case 17: // ctrl
23195             case 18: // alt
23196                 break;
23197             default :
23198                 this.hide();
23199                 
23200         }
23201     },
23202     
23203     remove: function() 
23204     {
23205         this.picker().remove();
23206     }
23207    
23208 });
23209
23210 Roo.apply(Roo.bootstrap.MonthField,  {
23211     
23212     content : {
23213         tag: 'tbody',
23214         cn: [
23215         {
23216             tag: 'tr',
23217             cn: [
23218             {
23219                 tag: 'td',
23220                 colspan: '7'
23221             }
23222             ]
23223         }
23224         ]
23225     },
23226     
23227     dates:{
23228         en: {
23229             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23230             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23231         }
23232     }
23233 });
23234
23235 Roo.apply(Roo.bootstrap.MonthField,  {
23236   
23237     template : {
23238         tag: 'div',
23239         cls: 'datepicker dropdown-menu roo-dynamic',
23240         cn: [
23241             {
23242                 tag: 'div',
23243                 cls: 'datepicker-months',
23244                 cn: [
23245                 {
23246                     tag: 'table',
23247                     cls: 'table-condensed',
23248                     cn:[
23249                         Roo.bootstrap.DateField.content
23250                     ]
23251                 }
23252                 ]
23253             }
23254         ]
23255     }
23256 });
23257
23258  
23259
23260  
23261  /*
23262  * - LGPL
23263  *
23264  * CheckBox
23265  * 
23266  */
23267
23268 /**
23269  * @class Roo.bootstrap.CheckBox
23270  * @extends Roo.bootstrap.Input
23271  * Bootstrap CheckBox class
23272  * 
23273  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23274  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23275  * @cfg {String} boxLabel The text that appears beside the checkbox
23276  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23277  * @cfg {Boolean} checked initnal the element
23278  * @cfg {Boolean} inline inline the element (default false)
23279  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23280  * @cfg {String} tooltip label tooltip
23281  * 
23282  * @constructor
23283  * Create a new CheckBox
23284  * @param {Object} config The config object
23285  */
23286
23287 Roo.bootstrap.CheckBox = function(config){
23288     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23289    
23290     this.addEvents({
23291         /**
23292         * @event check
23293         * Fires when the element is checked or unchecked.
23294         * @param {Roo.bootstrap.CheckBox} this This input
23295         * @param {Boolean} checked The new checked value
23296         */
23297        check : true,
23298        /**
23299         * @event click
23300         * Fires when the element is click.
23301         * @param {Roo.bootstrap.CheckBox} this This input
23302         */
23303        click : true
23304     });
23305     
23306 };
23307
23308 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23309   
23310     inputType: 'checkbox',
23311     inputValue: 1,
23312     valueOff: 0,
23313     boxLabel: false,
23314     checked: false,
23315     weight : false,
23316     inline: false,
23317     tooltip : '',
23318     
23319     // checkbox success does not make any sense really.. 
23320     invalidClass : "",
23321     validClass : "",
23322     
23323     
23324     getAutoCreate : function()
23325     {
23326         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23327         
23328         var id = Roo.id();
23329         
23330         var cfg = {};
23331         
23332         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23333         
23334         if(this.inline){
23335             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23336         }
23337         
23338         var input =  {
23339             tag: 'input',
23340             id : id,
23341             type : this.inputType,
23342             value : this.inputValue,
23343             cls : 'roo-' + this.inputType, //'form-box',
23344             placeholder : this.placeholder || ''
23345             
23346         };
23347         
23348         if(this.inputType != 'radio'){
23349             var hidden =  {
23350                 tag: 'input',
23351                 type : 'hidden',
23352                 cls : 'roo-hidden-value',
23353                 value : this.checked ? this.inputValue : this.valueOff
23354             };
23355         }
23356         
23357             
23358         if (this.weight) { // Validity check?
23359             cfg.cls += " " + this.inputType + "-" + this.weight;
23360         }
23361         
23362         if (this.disabled) {
23363             input.disabled=true;
23364         }
23365         
23366         if(this.checked){
23367             input.checked = this.checked;
23368         }
23369         
23370         if (this.name) {
23371             
23372             input.name = this.name;
23373             
23374             if(this.inputType != 'radio'){
23375                 hidden.name = this.name;
23376                 input.name = '_hidden_' + this.name;
23377             }
23378         }
23379         
23380         if (this.size) {
23381             input.cls += ' input-' + this.size;
23382         }
23383         
23384         var settings=this;
23385         
23386         ['xs','sm','md','lg'].map(function(size){
23387             if (settings[size]) {
23388                 cfg.cls += ' col-' + size + '-' + settings[size];
23389             }
23390         });
23391         
23392         var inputblock = input;
23393          
23394         if (this.before || this.after) {
23395             
23396             inputblock = {
23397                 cls : 'input-group',
23398                 cn :  [] 
23399             };
23400             
23401             if (this.before) {
23402                 inputblock.cn.push({
23403                     tag :'span',
23404                     cls : 'input-group-addon',
23405                     html : this.before
23406                 });
23407             }
23408             
23409             inputblock.cn.push(input);
23410             
23411             if(this.inputType != 'radio'){
23412                 inputblock.cn.push(hidden);
23413             }
23414             
23415             if (this.after) {
23416                 inputblock.cn.push({
23417                     tag :'span',
23418                     cls : 'input-group-addon',
23419                     html : this.after
23420                 });
23421             }
23422             
23423         }
23424         var boxLabelCfg = false;
23425         
23426         if(this.boxLabel){
23427            
23428             boxLabelCfg = {
23429                 tag: 'label',
23430                 //'for': id, // box label is handled by onclick - so no for...
23431                 cls: 'box-label',
23432                 html: this.boxLabel
23433             };
23434             if(this.tooltip){
23435                 boxLabelCfg.tooltip = this.tooltip;
23436             }
23437              
23438         }
23439         
23440         
23441         if (align ==='left' && this.fieldLabel.length) {
23442 //                Roo.log("left and has label");
23443             cfg.cn = [
23444                 {
23445                     tag: 'label',
23446                     'for' :  id,
23447                     cls : 'control-label',
23448                     html : this.fieldLabel
23449                 },
23450                 {
23451                     cls : "", 
23452                     cn: [
23453                         inputblock
23454                     ]
23455                 }
23456             ];
23457             
23458             if (boxLabelCfg) {
23459                 cfg.cn[1].cn.push(boxLabelCfg);
23460             }
23461             
23462             if(this.labelWidth > 12){
23463                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23464             }
23465             
23466             if(this.labelWidth < 13 && this.labelmd == 0){
23467                 this.labelmd = this.labelWidth;
23468             }
23469             
23470             if(this.labellg > 0){
23471                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23472                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23473             }
23474             
23475             if(this.labelmd > 0){
23476                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23477                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23478             }
23479             
23480             if(this.labelsm > 0){
23481                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23482                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23483             }
23484             
23485             if(this.labelxs > 0){
23486                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23487                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23488             }
23489             
23490         } else if ( this.fieldLabel.length) {
23491 //                Roo.log(" label");
23492                 cfg.cn = [
23493                    
23494                     {
23495                         tag: this.boxLabel ? 'span' : 'label',
23496                         'for': id,
23497                         cls: 'control-label box-input-label',
23498                         //cls : 'input-group-addon',
23499                         html : this.fieldLabel
23500                     },
23501                     
23502                     inputblock
23503                     
23504                 ];
23505                 if (boxLabelCfg) {
23506                     cfg.cn.push(boxLabelCfg);
23507                 }
23508
23509         } else {
23510             
23511 //                Roo.log(" no label && no align");
23512                 cfg.cn = [  inputblock ] ;
23513                 if (boxLabelCfg) {
23514                     cfg.cn.push(boxLabelCfg);
23515                 }
23516
23517                 
23518         }
23519         
23520        
23521         
23522         if(this.inputType != 'radio'){
23523             cfg.cn.push(hidden);
23524         }
23525         
23526         return cfg;
23527         
23528     },
23529     
23530     /**
23531      * return the real input element.
23532      */
23533     inputEl: function ()
23534     {
23535         return this.el.select('input.roo-' + this.inputType,true).first();
23536     },
23537     hiddenEl: function ()
23538     {
23539         return this.el.select('input.roo-hidden-value',true).first();
23540     },
23541     
23542     labelEl: function()
23543     {
23544         return this.el.select('label.control-label',true).first();
23545     },
23546     /* depricated... */
23547     
23548     label: function()
23549     {
23550         return this.labelEl();
23551     },
23552     
23553     boxLabelEl: function()
23554     {
23555         return this.el.select('label.box-label',true).first();
23556     },
23557     
23558     initEvents : function()
23559     {
23560 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23561         
23562         this.inputEl().on('click', this.onClick,  this);
23563         
23564         if (this.boxLabel) { 
23565             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23566         }
23567         
23568         this.startValue = this.getValue();
23569         
23570         if(this.groupId){
23571             Roo.bootstrap.CheckBox.register(this);
23572         }
23573     },
23574     
23575     onClick : function(e)
23576     {   
23577         if(this.fireEvent('click', this, e) !== false){
23578             this.setChecked(!this.checked);
23579         }
23580         
23581     },
23582     
23583     setChecked : function(state,suppressEvent)
23584     {
23585         this.startValue = this.getValue();
23586
23587         if(this.inputType == 'radio'){
23588             
23589             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23590                 e.dom.checked = false;
23591             });
23592             
23593             this.inputEl().dom.checked = true;
23594             
23595             this.inputEl().dom.value = this.inputValue;
23596             
23597             if(suppressEvent !== true){
23598                 this.fireEvent('check', this, true);
23599             }
23600             
23601             this.validate();
23602             
23603             return;
23604         }
23605         
23606         this.checked = state;
23607         
23608         this.inputEl().dom.checked = state;
23609         
23610         
23611         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23612         
23613         if(suppressEvent !== true){
23614             this.fireEvent('check', this, state);
23615         }
23616         
23617         this.validate();
23618     },
23619     
23620     getValue : function()
23621     {
23622         if(this.inputType == 'radio'){
23623             return this.getGroupValue();
23624         }
23625         
23626         return this.hiddenEl().dom.value;
23627         
23628     },
23629     
23630     getGroupValue : function()
23631     {
23632         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23633             return '';
23634         }
23635         
23636         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23637     },
23638     
23639     setValue : function(v,suppressEvent)
23640     {
23641         if(this.inputType == 'radio'){
23642             this.setGroupValue(v, suppressEvent);
23643             return;
23644         }
23645         
23646         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23647         
23648         this.validate();
23649     },
23650     
23651     setGroupValue : function(v, suppressEvent)
23652     {
23653         this.startValue = this.getValue();
23654         
23655         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23656             e.dom.checked = false;
23657             
23658             if(e.dom.value == v){
23659                 e.dom.checked = true;
23660             }
23661         });
23662         
23663         if(suppressEvent !== true){
23664             this.fireEvent('check', this, true);
23665         }
23666
23667         this.validate();
23668         
23669         return;
23670     },
23671     
23672     validate : function()
23673     {
23674         if(this.getVisibilityEl().hasClass('hidden')){
23675             return true;
23676         }
23677         
23678         if(
23679                 this.disabled || 
23680                 (this.inputType == 'radio' && this.validateRadio()) ||
23681                 (this.inputType == 'checkbox' && this.validateCheckbox())
23682         ){
23683             this.markValid();
23684             return true;
23685         }
23686         
23687         this.markInvalid();
23688         return false;
23689     },
23690     
23691     validateRadio : function()
23692     {
23693         if(this.getVisibilityEl().hasClass('hidden')){
23694             return true;
23695         }
23696         
23697         if(this.allowBlank){
23698             return true;
23699         }
23700         
23701         var valid = false;
23702         
23703         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23704             if(!e.dom.checked){
23705                 return;
23706             }
23707             
23708             valid = true;
23709             
23710             return false;
23711         });
23712         
23713         return valid;
23714     },
23715     
23716     validateCheckbox : function()
23717     {
23718         if(!this.groupId){
23719             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23720             //return (this.getValue() == this.inputValue) ? true : false;
23721         }
23722         
23723         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23724         
23725         if(!group){
23726             return false;
23727         }
23728         
23729         var r = false;
23730         
23731         for(var i in group){
23732             if(group[i].el.isVisible(true)){
23733                 r = false;
23734                 break;
23735             }
23736             
23737             r = true;
23738         }
23739         
23740         for(var i in group){
23741             if(r){
23742                 break;
23743             }
23744             
23745             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23746         }
23747         
23748         return r;
23749     },
23750     
23751     /**
23752      * Mark this field as valid
23753      */
23754     markValid : function()
23755     {
23756         var _this = this;
23757         
23758         this.fireEvent('valid', this);
23759         
23760         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23761         
23762         if(this.groupId){
23763             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23764         }
23765         
23766         if(label){
23767             label.markValid();
23768         }
23769
23770         if(this.inputType == 'radio'){
23771             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23772                 var fg = e.findParent('.form-group', false, true);
23773                 if (Roo.bootstrap.version == 3) {
23774                     fg.removeClass([_this.invalidClass, _this.validClass]);
23775                     fg.addClass(_this.validClass);
23776                 } else {
23777                     fg.removeClass(['is-valid', 'is-invalid']);
23778                     fg.addClass('is-valid');
23779                 }
23780             });
23781             
23782             return;
23783         }
23784
23785         if(!this.groupId){
23786             var fg = this.el.findParent('.form-group', false, true);
23787             if (Roo.bootstrap.version == 3) {
23788                 fg.removeClass([this.invalidClass, this.validClass]);
23789                 fg.addClass(this.validClass);
23790             } else {
23791                 fg.removeClass(['is-valid', 'is-invalid']);
23792                 fg.addClass('is-valid');
23793             }
23794             return;
23795         }
23796         
23797         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23798         
23799         if(!group){
23800             return;
23801         }
23802         
23803         for(var i in group){
23804             var fg = group[i].el.findParent('.form-group', false, true);
23805             if (Roo.bootstrap.version == 3) {
23806                 fg.removeClass([this.invalidClass, this.validClass]);
23807                 fg.addClass(this.validClass);
23808             } else {
23809                 fg.removeClass(['is-valid', 'is-invalid']);
23810                 fg.addClass('is-valid');
23811             }
23812         }
23813     },
23814     
23815      /**
23816      * Mark this field as invalid
23817      * @param {String} msg The validation message
23818      */
23819     markInvalid : function(msg)
23820     {
23821         if(this.allowBlank){
23822             return;
23823         }
23824         
23825         var _this = this;
23826         
23827         this.fireEvent('invalid', this, msg);
23828         
23829         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23830         
23831         if(this.groupId){
23832             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23833         }
23834         
23835         if(label){
23836             label.markInvalid();
23837         }
23838             
23839         if(this.inputType == 'radio'){
23840             
23841             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23842                 var fg = e.findParent('.form-group', false, true);
23843                 if (Roo.bootstrap.version == 3) {
23844                     fg.removeClass([_this.invalidClass, _this.validClass]);
23845                     fg.addClass(_this.invalidClass);
23846                 } else {
23847                     fg.removeClass(['is-invalid', 'is-valid']);
23848                     fg.addClass('is-invalid');
23849                 }
23850             });
23851             
23852             return;
23853         }
23854         
23855         if(!this.groupId){
23856             var fg = this.el.findParent('.form-group', false, true);
23857             if (Roo.bootstrap.version == 3) {
23858                 fg.removeClass([_this.invalidClass, _this.validClass]);
23859                 fg.addClass(_this.invalidClass);
23860             } else {
23861                 fg.removeClass(['is-invalid', 'is-valid']);
23862                 fg.addClass('is-invalid');
23863             }
23864             return;
23865         }
23866         
23867         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23868         
23869         if(!group){
23870             return;
23871         }
23872         
23873         for(var i in group){
23874             var fg = group[i].el.findParent('.form-group', false, true);
23875             if (Roo.bootstrap.version == 3) {
23876                 fg.removeClass([_this.invalidClass, _this.validClass]);
23877                 fg.addClass(_this.invalidClass);
23878             } else {
23879                 fg.removeClass(['is-invalid', 'is-valid']);
23880                 fg.addClass('is-invalid');
23881             }
23882         }
23883         
23884     },
23885     
23886     clearInvalid : function()
23887     {
23888         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23889         
23890         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23891         
23892         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23893         
23894         if (label && label.iconEl) {
23895             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23896             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23897         }
23898     },
23899     
23900     disable : function()
23901     {
23902         if(this.inputType != 'radio'){
23903             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23904             return;
23905         }
23906         
23907         var _this = this;
23908         
23909         if(this.rendered){
23910             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23911                 _this.getActionEl().addClass(this.disabledClass);
23912                 e.dom.disabled = true;
23913             });
23914         }
23915         
23916         this.disabled = true;
23917         this.fireEvent("disable", this);
23918         return this;
23919     },
23920
23921     enable : function()
23922     {
23923         if(this.inputType != 'radio'){
23924             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23925             return;
23926         }
23927         
23928         var _this = this;
23929         
23930         if(this.rendered){
23931             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23932                 _this.getActionEl().removeClass(this.disabledClass);
23933                 e.dom.disabled = false;
23934             });
23935         }
23936         
23937         this.disabled = false;
23938         this.fireEvent("enable", this);
23939         return this;
23940     },
23941     
23942     setBoxLabel : function(v)
23943     {
23944         this.boxLabel = v;
23945         
23946         if(this.rendered){
23947             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23948         }
23949     }
23950
23951 });
23952
23953 Roo.apply(Roo.bootstrap.CheckBox, {
23954     
23955     groups: {},
23956     
23957      /**
23958     * register a CheckBox Group
23959     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23960     */
23961     register : function(checkbox)
23962     {
23963         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23964             this.groups[checkbox.groupId] = {};
23965         }
23966         
23967         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23968             return;
23969         }
23970         
23971         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23972         
23973     },
23974     /**
23975     * fetch a CheckBox Group based on the group ID
23976     * @param {string} the group ID
23977     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23978     */
23979     get: function(groupId) {
23980         if (typeof(this.groups[groupId]) == 'undefined') {
23981             return false;
23982         }
23983         
23984         return this.groups[groupId] ;
23985     }
23986     
23987     
23988 });
23989 /*
23990  * - LGPL
23991  *
23992  * RadioItem
23993  * 
23994  */
23995
23996 /**
23997  * @class Roo.bootstrap.Radio
23998  * @extends Roo.bootstrap.Component
23999  * Bootstrap Radio class
24000  * @cfg {String} boxLabel - the label associated
24001  * @cfg {String} value - the value of radio
24002  * 
24003  * @constructor
24004  * Create a new Radio
24005  * @param {Object} config The config object
24006  */
24007 Roo.bootstrap.Radio = function(config){
24008     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24009     
24010 };
24011
24012 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24013     
24014     boxLabel : '',
24015     
24016     value : '',
24017     
24018     getAutoCreate : function()
24019     {
24020         var cfg = {
24021             tag : 'div',
24022             cls : 'form-group radio',
24023             cn : [
24024                 {
24025                     tag : 'label',
24026                     cls : 'box-label',
24027                     html : this.boxLabel
24028                 }
24029             ]
24030         };
24031         
24032         return cfg;
24033     },
24034     
24035     initEvents : function() 
24036     {
24037         this.parent().register(this);
24038         
24039         this.el.on('click', this.onClick, this);
24040         
24041     },
24042     
24043     onClick : function(e)
24044     {
24045         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24046             this.setChecked(true);
24047         }
24048     },
24049     
24050     setChecked : function(state, suppressEvent)
24051     {
24052         this.parent().setValue(this.value, suppressEvent);
24053         
24054     },
24055     
24056     setBoxLabel : function(v)
24057     {
24058         this.boxLabel = v;
24059         
24060         if(this.rendered){
24061             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24062         }
24063     }
24064     
24065 });
24066  
24067
24068  /*
24069  * - LGPL
24070  *
24071  * Input
24072  * 
24073  */
24074
24075 /**
24076  * @class Roo.bootstrap.SecurePass
24077  * @extends Roo.bootstrap.Input
24078  * Bootstrap SecurePass class
24079  *
24080  * 
24081  * @constructor
24082  * Create a new SecurePass
24083  * @param {Object} config The config object
24084  */
24085  
24086 Roo.bootstrap.SecurePass = function (config) {
24087     // these go here, so the translation tool can replace them..
24088     this.errors = {
24089         PwdEmpty: "Please type a password, and then retype it to confirm.",
24090         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24091         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24092         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24093         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24094         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24095         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24096         TooWeak: "Your password is Too Weak."
24097     },
24098     this.meterLabel = "Password strength:";
24099     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24100     this.meterClass = [
24101         "roo-password-meter-tooweak", 
24102         "roo-password-meter-weak", 
24103         "roo-password-meter-medium", 
24104         "roo-password-meter-strong", 
24105         "roo-password-meter-grey"
24106     ];
24107     
24108     this.errors = {};
24109     
24110     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24111 }
24112
24113 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24114     /**
24115      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24116      * {
24117      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24118      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24119      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24120      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24121      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24122      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24123      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24124      * })
24125      */
24126     // private
24127     
24128     meterWidth: 300,
24129     errorMsg :'',    
24130     errors: false,
24131     imageRoot: '/',
24132     /**
24133      * @cfg {String/Object} Label for the strength meter (defaults to
24134      * 'Password strength:')
24135      */
24136     // private
24137     meterLabel: '',
24138     /**
24139      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24140      * ['Weak', 'Medium', 'Strong'])
24141      */
24142     // private    
24143     pwdStrengths: false,    
24144     // private
24145     strength: 0,
24146     // private
24147     _lastPwd: null,
24148     // private
24149     kCapitalLetter: 0,
24150     kSmallLetter: 1,
24151     kDigit: 2,
24152     kPunctuation: 3,
24153     
24154     insecure: false,
24155     // private
24156     initEvents: function ()
24157     {
24158         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24159
24160         if (this.el.is('input[type=password]') && Roo.isSafari) {
24161             this.el.on('keydown', this.SafariOnKeyDown, this);
24162         }
24163
24164         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24165     },
24166     // private
24167     onRender: function (ct, position)
24168     {
24169         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24170         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24171         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24172
24173         this.trigger.createChild({
24174                    cn: [
24175                     {
24176                     //id: 'PwdMeter',
24177                     tag: 'div',
24178                     cls: 'roo-password-meter-grey col-xs-12',
24179                     style: {
24180                         //width: 0,
24181                         //width: this.meterWidth + 'px'                                                
24182                         }
24183                     },
24184                     {                            
24185                          cls: 'roo-password-meter-text'                          
24186                     }
24187                 ]            
24188         });
24189
24190          
24191         if (this.hideTrigger) {
24192             this.trigger.setDisplayed(false);
24193         }
24194         this.setSize(this.width || '', this.height || '');
24195     },
24196     // private
24197     onDestroy: function ()
24198     {
24199         if (this.trigger) {
24200             this.trigger.removeAllListeners();
24201             this.trigger.remove();
24202         }
24203         if (this.wrap) {
24204             this.wrap.remove();
24205         }
24206         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24207     },
24208     // private
24209     checkStrength: function ()
24210     {
24211         var pwd = this.inputEl().getValue();
24212         if (pwd == this._lastPwd) {
24213             return;
24214         }
24215
24216         var strength;
24217         if (this.ClientSideStrongPassword(pwd)) {
24218             strength = 3;
24219         } else if (this.ClientSideMediumPassword(pwd)) {
24220             strength = 2;
24221         } else if (this.ClientSideWeakPassword(pwd)) {
24222             strength = 1;
24223         } else {
24224             strength = 0;
24225         }
24226         
24227         Roo.log('strength1: ' + strength);
24228         
24229         //var pm = this.trigger.child('div/div/div').dom;
24230         var pm = this.trigger.child('div/div');
24231         pm.removeClass(this.meterClass);
24232         pm.addClass(this.meterClass[strength]);
24233                 
24234         
24235         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24236                 
24237         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24238         
24239         this._lastPwd = pwd;
24240     },
24241     reset: function ()
24242     {
24243         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24244         
24245         this._lastPwd = '';
24246         
24247         var pm = this.trigger.child('div/div');
24248         pm.removeClass(this.meterClass);
24249         pm.addClass('roo-password-meter-grey');        
24250         
24251         
24252         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24253         
24254         pt.innerHTML = '';
24255         this.inputEl().dom.type='password';
24256     },
24257     // private
24258     validateValue: function (value)
24259     {
24260         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24261             return false;
24262         }
24263         if (value.length == 0) {
24264             if (this.allowBlank) {
24265                 this.clearInvalid();
24266                 return true;
24267             }
24268
24269             this.markInvalid(this.errors.PwdEmpty);
24270             this.errorMsg = this.errors.PwdEmpty;
24271             return false;
24272         }
24273         
24274         if(this.insecure){
24275             return true;
24276         }
24277         
24278         if (!value.match(/[\x21-\x7e]+/)) {
24279             this.markInvalid(this.errors.PwdBadChar);
24280             this.errorMsg = this.errors.PwdBadChar;
24281             return false;
24282         }
24283         if (value.length < 6) {
24284             this.markInvalid(this.errors.PwdShort);
24285             this.errorMsg = this.errors.PwdShort;
24286             return false;
24287         }
24288         if (value.length > 16) {
24289             this.markInvalid(this.errors.PwdLong);
24290             this.errorMsg = this.errors.PwdLong;
24291             return false;
24292         }
24293         var strength;
24294         if (this.ClientSideStrongPassword(value)) {
24295             strength = 3;
24296         } else if (this.ClientSideMediumPassword(value)) {
24297             strength = 2;
24298         } else if (this.ClientSideWeakPassword(value)) {
24299             strength = 1;
24300         } else {
24301             strength = 0;
24302         }
24303
24304         
24305         if (strength < 2) {
24306             //this.markInvalid(this.errors.TooWeak);
24307             this.errorMsg = this.errors.TooWeak;
24308             //return false;
24309         }
24310         
24311         
24312         console.log('strength2: ' + strength);
24313         
24314         //var pm = this.trigger.child('div/div/div').dom;
24315         
24316         var pm = this.trigger.child('div/div');
24317         pm.removeClass(this.meterClass);
24318         pm.addClass(this.meterClass[strength]);
24319                 
24320         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24321                 
24322         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24323         
24324         this.errorMsg = ''; 
24325         return true;
24326     },
24327     // private
24328     CharacterSetChecks: function (type)
24329     {
24330         this.type = type;
24331         this.fResult = false;
24332     },
24333     // private
24334     isctype: function (character, type)
24335     {
24336         switch (type) {  
24337             case this.kCapitalLetter:
24338                 if (character >= 'A' && character <= 'Z') {
24339                     return true;
24340                 }
24341                 break;
24342             
24343             case this.kSmallLetter:
24344                 if (character >= 'a' && character <= 'z') {
24345                     return true;
24346                 }
24347                 break;
24348             
24349             case this.kDigit:
24350                 if (character >= '0' && character <= '9') {
24351                     return true;
24352                 }
24353                 break;
24354             
24355             case this.kPunctuation:
24356                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24357                     return true;
24358                 }
24359                 break;
24360             
24361             default:
24362                 return false;
24363         }
24364
24365     },
24366     // private
24367     IsLongEnough: function (pwd, size)
24368     {
24369         return !(pwd == null || isNaN(size) || pwd.length < size);
24370     },
24371     // private
24372     SpansEnoughCharacterSets: function (word, nb)
24373     {
24374         if (!this.IsLongEnough(word, nb))
24375         {
24376             return false;
24377         }
24378
24379         var characterSetChecks = new Array(
24380             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24381             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24382         );
24383         
24384         for (var index = 0; index < word.length; ++index) {
24385             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24386                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24387                     characterSetChecks[nCharSet].fResult = true;
24388                     break;
24389                 }
24390             }
24391         }
24392
24393         var nCharSets = 0;
24394         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24395             if (characterSetChecks[nCharSet].fResult) {
24396                 ++nCharSets;
24397             }
24398         }
24399
24400         if (nCharSets < nb) {
24401             return false;
24402         }
24403         return true;
24404     },
24405     // private
24406     ClientSideStrongPassword: function (pwd)
24407     {
24408         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24409     },
24410     // private
24411     ClientSideMediumPassword: function (pwd)
24412     {
24413         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24414     },
24415     // private
24416     ClientSideWeakPassword: function (pwd)
24417     {
24418         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24419     }
24420           
24421 })//<script type="text/javascript">
24422
24423 /*
24424  * Based  Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  * LGPL
24427  *
24428  */
24429  
24430 /**
24431  * @class Roo.HtmlEditorCore
24432  * @extends Roo.Component
24433  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24434  *
24435  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24436  */
24437
24438 Roo.HtmlEditorCore = function(config){
24439     
24440     
24441     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24442     
24443     
24444     this.addEvents({
24445         /**
24446          * @event initialize
24447          * Fires when the editor is fully initialized (including the iframe)
24448          * @param {Roo.HtmlEditorCore} this
24449          */
24450         initialize: true,
24451         /**
24452          * @event activate
24453          * Fires when the editor is first receives the focus. Any insertion must wait
24454          * until after this event.
24455          * @param {Roo.HtmlEditorCore} this
24456          */
24457         activate: true,
24458          /**
24459          * @event beforesync
24460          * Fires before the textarea is updated with content from the editor iframe. Return false
24461          * to cancel the sync.
24462          * @param {Roo.HtmlEditorCore} this
24463          * @param {String} html
24464          */
24465         beforesync: true,
24466          /**
24467          * @event beforepush
24468          * Fires before the iframe editor is updated with content from the textarea. Return false
24469          * to cancel the push.
24470          * @param {Roo.HtmlEditorCore} this
24471          * @param {String} html
24472          */
24473         beforepush: true,
24474          /**
24475          * @event sync
24476          * Fires when the textarea is updated with content from the editor iframe.
24477          * @param {Roo.HtmlEditorCore} this
24478          * @param {String} html
24479          */
24480         sync: true,
24481          /**
24482          * @event push
24483          * Fires when the iframe editor is updated with content from the textarea.
24484          * @param {Roo.HtmlEditorCore} this
24485          * @param {String} html
24486          */
24487         push: true,
24488         
24489         /**
24490          * @event editorevent
24491          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24492          * @param {Roo.HtmlEditorCore} this
24493          */
24494         editorevent: true
24495         
24496     });
24497     
24498     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24499     
24500     // defaults : white / black...
24501     this.applyBlacklists();
24502     
24503     
24504     
24505 };
24506
24507
24508 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24509
24510
24511      /**
24512      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24513      */
24514     
24515     owner : false,
24516     
24517      /**
24518      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24519      *                        Roo.resizable.
24520      */
24521     resizable : false,
24522      /**
24523      * @cfg {Number} height (in pixels)
24524      */   
24525     height: 300,
24526    /**
24527      * @cfg {Number} width (in pixels)
24528      */   
24529     width: 500,
24530     
24531     /**
24532      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24533      * 
24534      */
24535     stylesheets: false,
24536     
24537     // id of frame..
24538     frameId: false,
24539     
24540     // private properties
24541     validationEvent : false,
24542     deferHeight: true,
24543     initialized : false,
24544     activated : false,
24545     sourceEditMode : false,
24546     onFocus : Roo.emptyFn,
24547     iframePad:3,
24548     hideMode:'offsets',
24549     
24550     clearUp: true,
24551     
24552     // blacklist + whitelisted elements..
24553     black: false,
24554     white: false,
24555      
24556     bodyCls : '',
24557
24558     /**
24559      * Protected method that will not generally be called directly. It
24560      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24561      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24562      */
24563     getDocMarkup : function(){
24564         // body styles..
24565         var st = '';
24566         
24567         // inherit styels from page...?? 
24568         if (this.stylesheets === false) {
24569             
24570             Roo.get(document.head).select('style').each(function(node) {
24571                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24572             });
24573             
24574             Roo.get(document.head).select('link').each(function(node) { 
24575                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24576             });
24577             
24578         } else if (!this.stylesheets.length) {
24579                 // simple..
24580                 st = '<style type="text/css">' +
24581                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24582                    '</style>';
24583         } else {
24584             for (var i in this.stylesheets) { 
24585                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24586             }
24587             
24588         }
24589         
24590         st +=  '<style type="text/css">' +
24591             'IMG { cursor: pointer } ' +
24592         '</style>';
24593
24594         var cls = 'roo-htmleditor-body';
24595         
24596         if(this.bodyCls.length){
24597             cls += ' ' + this.bodyCls;
24598         }
24599         
24600         return '<html><head>' + st  +
24601             //<style type="text/css">' +
24602             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24603             //'</style>' +
24604             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24605     },
24606
24607     // private
24608     onRender : function(ct, position)
24609     {
24610         var _t = this;
24611         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24612         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24613         
24614         
24615         this.el.dom.style.border = '0 none';
24616         this.el.dom.setAttribute('tabIndex', -1);
24617         this.el.addClass('x-hidden hide');
24618         
24619         
24620         
24621         if(Roo.isIE){ // fix IE 1px bogus margin
24622             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24623         }
24624        
24625         
24626         this.frameId = Roo.id();
24627         
24628          
24629         
24630         var iframe = this.owner.wrap.createChild({
24631             tag: 'iframe',
24632             cls: 'form-control', // bootstrap..
24633             id: this.frameId,
24634             name: this.frameId,
24635             frameBorder : 'no',
24636             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24637         }, this.el
24638         );
24639         
24640         
24641         this.iframe = iframe.dom;
24642
24643          this.assignDocWin();
24644         
24645         this.doc.designMode = 'on';
24646        
24647         this.doc.open();
24648         this.doc.write(this.getDocMarkup());
24649         this.doc.close();
24650
24651         
24652         var task = { // must defer to wait for browser to be ready
24653             run : function(){
24654                 //console.log("run task?" + this.doc.readyState);
24655                 this.assignDocWin();
24656                 if(this.doc.body || this.doc.readyState == 'complete'){
24657                     try {
24658                         this.doc.designMode="on";
24659                     } catch (e) {
24660                         return;
24661                     }
24662                     Roo.TaskMgr.stop(task);
24663                     this.initEditor.defer(10, this);
24664                 }
24665             },
24666             interval : 10,
24667             duration: 10000,
24668             scope: this
24669         };
24670         Roo.TaskMgr.start(task);
24671
24672     },
24673
24674     // private
24675     onResize : function(w, h)
24676     {
24677          Roo.log('resize: ' +w + ',' + h );
24678         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24679         if(!this.iframe){
24680             return;
24681         }
24682         if(typeof w == 'number'){
24683             
24684             this.iframe.style.width = w + 'px';
24685         }
24686         if(typeof h == 'number'){
24687             
24688             this.iframe.style.height = h + 'px';
24689             if(this.doc){
24690                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24691             }
24692         }
24693         
24694     },
24695
24696     /**
24697      * Toggles the editor between standard and source edit mode.
24698      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24699      */
24700     toggleSourceEdit : function(sourceEditMode){
24701         
24702         this.sourceEditMode = sourceEditMode === true;
24703         
24704         if(this.sourceEditMode){
24705  
24706             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24707             
24708         }else{
24709             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24710             //this.iframe.className = '';
24711             this.deferFocus();
24712         }
24713         //this.setSize(this.owner.wrap.getSize());
24714         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24715     },
24716
24717     
24718   
24719
24720     /**
24721      * Protected method that will not generally be called directly. If you need/want
24722      * custom HTML cleanup, this is the method you should override.
24723      * @param {String} html The HTML to be cleaned
24724      * return {String} The cleaned HTML
24725      */
24726     cleanHtml : function(html){
24727         html = String(html);
24728         if(html.length > 5){
24729             if(Roo.isSafari){ // strip safari nonsense
24730                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24731             }
24732         }
24733         if(html == '&nbsp;'){
24734             html = '';
24735         }
24736         return html;
24737     },
24738
24739     /**
24740      * HTML Editor -> Textarea
24741      * Protected method that will not generally be called directly. Syncs the contents
24742      * of the editor iframe with the textarea.
24743      */
24744     syncValue : function(){
24745         if(this.initialized){
24746             var bd = (this.doc.body || this.doc.documentElement);
24747             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24748             var html = bd.innerHTML;
24749             if(Roo.isSafari){
24750                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24751                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24752                 if(m && m[1]){
24753                     html = '<div style="'+m[0]+'">' + html + '</div>';
24754                 }
24755             }
24756             html = this.cleanHtml(html);
24757             // fix up the special chars.. normaly like back quotes in word...
24758             // however we do not want to do this with chinese..
24759             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24760                 
24761                 var cc = match.charCodeAt();
24762
24763                 // Get the character value, handling surrogate pairs
24764                 if (match.length == 2) {
24765                     // It's a surrogate pair, calculate the Unicode code point
24766                     var high = match.charCodeAt(0) - 0xD800;
24767                     var low  = match.charCodeAt(1) - 0xDC00;
24768                     cc = (high * 0x400) + low + 0x10000;
24769                 }  else if (
24770                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24771                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24772                     (cc >= 0xf900 && cc < 0xfb00 )
24773                 ) {
24774                         return match;
24775                 }  
24776          
24777                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24778                 return "&#" + cc + ";";
24779                 
24780                 
24781             });
24782             
24783             
24784              
24785             if(this.owner.fireEvent('beforesync', this, html) !== false){
24786                 this.el.dom.value = html;
24787                 this.owner.fireEvent('sync', this, html);
24788             }
24789         }
24790     },
24791
24792     /**
24793      * Protected method that will not generally be called directly. Pushes the value of the textarea
24794      * into the iframe editor.
24795      */
24796     pushValue : function(){
24797         if(this.initialized){
24798             var v = this.el.dom.value.trim();
24799             
24800 //            if(v.length < 1){
24801 //                v = '&#160;';
24802 //            }
24803             
24804             if(this.owner.fireEvent('beforepush', this, v) !== false){
24805                 var d = (this.doc.body || this.doc.documentElement);
24806                 d.innerHTML = v;
24807                 this.cleanUpPaste();
24808                 this.el.dom.value = d.innerHTML;
24809                 this.owner.fireEvent('push', this, v);
24810             }
24811         }
24812     },
24813
24814     // private
24815     deferFocus : function(){
24816         this.focus.defer(10, this);
24817     },
24818
24819     // doc'ed in Field
24820     focus : function(){
24821         if(this.win && !this.sourceEditMode){
24822             this.win.focus();
24823         }else{
24824             this.el.focus();
24825         }
24826     },
24827     
24828     assignDocWin: function()
24829     {
24830         var iframe = this.iframe;
24831         
24832          if(Roo.isIE){
24833             this.doc = iframe.contentWindow.document;
24834             this.win = iframe.contentWindow;
24835         } else {
24836 //            if (!Roo.get(this.frameId)) {
24837 //                return;
24838 //            }
24839 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24840 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24841             
24842             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24843                 return;
24844             }
24845             
24846             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24847             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24848         }
24849     },
24850     
24851     // private
24852     initEditor : function(){
24853         //console.log("INIT EDITOR");
24854         this.assignDocWin();
24855         
24856         
24857         
24858         this.doc.designMode="on";
24859         this.doc.open();
24860         this.doc.write(this.getDocMarkup());
24861         this.doc.close();
24862         
24863         var dbody = (this.doc.body || this.doc.documentElement);
24864         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24865         // this copies styles from the containing element into thsi one..
24866         // not sure why we need all of this..
24867         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24868         
24869         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24870         //ss['background-attachment'] = 'fixed'; // w3c
24871         dbody.bgProperties = 'fixed'; // ie
24872         //Roo.DomHelper.applyStyles(dbody, ss);
24873         Roo.EventManager.on(this.doc, {
24874             //'mousedown': this.onEditorEvent,
24875             'mouseup': this.onEditorEvent,
24876             'dblclick': this.onEditorEvent,
24877             'click': this.onEditorEvent,
24878             'keyup': this.onEditorEvent,
24879             buffer:100,
24880             scope: this
24881         });
24882         if(Roo.isGecko){
24883             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24884         }
24885         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24886             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24887         }
24888         this.initialized = true;
24889
24890         this.owner.fireEvent('initialize', this);
24891         this.pushValue();
24892     },
24893
24894     // private
24895     onDestroy : function(){
24896         
24897         
24898         
24899         if(this.rendered){
24900             
24901             //for (var i =0; i < this.toolbars.length;i++) {
24902             //    // fixme - ask toolbars for heights?
24903             //    this.toolbars[i].onDestroy();
24904            // }
24905             
24906             //this.wrap.dom.innerHTML = '';
24907             //this.wrap.remove();
24908         }
24909     },
24910
24911     // private
24912     onFirstFocus : function(){
24913         
24914         this.assignDocWin();
24915         
24916         
24917         this.activated = true;
24918          
24919     
24920         if(Roo.isGecko){ // prevent silly gecko errors
24921             this.win.focus();
24922             var s = this.win.getSelection();
24923             if(!s.focusNode || s.focusNode.nodeType != 3){
24924                 var r = s.getRangeAt(0);
24925                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24926                 r.collapse(true);
24927                 this.deferFocus();
24928             }
24929             try{
24930                 this.execCmd('useCSS', true);
24931                 this.execCmd('styleWithCSS', false);
24932             }catch(e){}
24933         }
24934         this.owner.fireEvent('activate', this);
24935     },
24936
24937     // private
24938     adjustFont: function(btn){
24939         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24940         //if(Roo.isSafari){ // safari
24941         //    adjust *= 2;
24942        // }
24943         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24944         if(Roo.isSafari){ // safari
24945             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24946             v =  (v < 10) ? 10 : v;
24947             v =  (v > 48) ? 48 : v;
24948             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24949             
24950         }
24951         
24952         
24953         v = Math.max(1, v+adjust);
24954         
24955         this.execCmd('FontSize', v  );
24956     },
24957
24958     onEditorEvent : function(e)
24959     {
24960         this.owner.fireEvent('editorevent', this, e);
24961       //  this.updateToolbar();
24962         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24963     },
24964
24965     insertTag : function(tg)
24966     {
24967         // could be a bit smarter... -> wrap the current selected tRoo..
24968         if (tg.toLowerCase() == 'span' ||
24969             tg.toLowerCase() == 'code' ||
24970             tg.toLowerCase() == 'sup' ||
24971             tg.toLowerCase() == 'sub' 
24972             ) {
24973             
24974             range = this.createRange(this.getSelection());
24975             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24976             wrappingNode.appendChild(range.extractContents());
24977             range.insertNode(wrappingNode);
24978
24979             return;
24980             
24981             
24982             
24983         }
24984         this.execCmd("formatblock",   tg);
24985         
24986     },
24987     
24988     insertText : function(txt)
24989     {
24990         
24991         
24992         var range = this.createRange();
24993         range.deleteContents();
24994                //alert(Sender.getAttribute('label'));
24995                
24996         range.insertNode(this.doc.createTextNode(txt));
24997     } ,
24998     
24999      
25000
25001     /**
25002      * Executes a Midas editor command on the editor document and performs necessary focus and
25003      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25004      * @param {String} cmd The Midas command
25005      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25006      */
25007     relayCmd : function(cmd, value){
25008         this.win.focus();
25009         this.execCmd(cmd, value);
25010         this.owner.fireEvent('editorevent', this);
25011         //this.updateToolbar();
25012         this.owner.deferFocus();
25013     },
25014
25015     /**
25016      * Executes a Midas editor command directly on the editor document.
25017      * For visual commands, you should use {@link #relayCmd} instead.
25018      * <b>This should only be called after the editor is initialized.</b>
25019      * @param {String} cmd The Midas command
25020      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25021      */
25022     execCmd : function(cmd, value){
25023         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25024         this.syncValue();
25025     },
25026  
25027  
25028    
25029     /**
25030      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25031      * to insert tRoo.
25032      * @param {String} text | dom node.. 
25033      */
25034     insertAtCursor : function(text)
25035     {
25036         
25037         if(!this.activated){
25038             return;
25039         }
25040         /*
25041         if(Roo.isIE){
25042             this.win.focus();
25043             var r = this.doc.selection.createRange();
25044             if(r){
25045                 r.collapse(true);
25046                 r.pasteHTML(text);
25047                 this.syncValue();
25048                 this.deferFocus();
25049             
25050             }
25051             return;
25052         }
25053         */
25054         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25055             this.win.focus();
25056             
25057             
25058             // from jquery ui (MIT licenced)
25059             var range, node;
25060             var win = this.win;
25061             
25062             if (win.getSelection && win.getSelection().getRangeAt) {
25063                 range = win.getSelection().getRangeAt(0);
25064                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25065                 range.insertNode(node);
25066             } else if (win.document.selection && win.document.selection.createRange) {
25067                 // no firefox support
25068                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25069                 win.document.selection.createRange().pasteHTML(txt);
25070             } else {
25071                 // no firefox support
25072                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25073                 this.execCmd('InsertHTML', txt);
25074             } 
25075             
25076             this.syncValue();
25077             
25078             this.deferFocus();
25079         }
25080     },
25081  // private
25082     mozKeyPress : function(e){
25083         if(e.ctrlKey){
25084             var c = e.getCharCode(), cmd;
25085           
25086             if(c > 0){
25087                 c = String.fromCharCode(c).toLowerCase();
25088                 switch(c){
25089                     case 'b':
25090                         cmd = 'bold';
25091                         break;
25092                     case 'i':
25093                         cmd = 'italic';
25094                         break;
25095                     
25096                     case 'u':
25097                         cmd = 'underline';
25098                         break;
25099                     
25100                     case 'v':
25101                         this.cleanUpPaste.defer(100, this);
25102                         return;
25103                         
25104                 }
25105                 if(cmd){
25106                     this.win.focus();
25107                     this.execCmd(cmd);
25108                     this.deferFocus();
25109                     e.preventDefault();
25110                 }
25111                 
25112             }
25113         }
25114     },
25115
25116     // private
25117     fixKeys : function(){ // load time branching for fastest keydown performance
25118         if(Roo.isIE){
25119             return function(e){
25120                 var k = e.getKey(), r;
25121                 if(k == e.TAB){
25122                     e.stopEvent();
25123                     r = this.doc.selection.createRange();
25124                     if(r){
25125                         r.collapse(true);
25126                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25127                         this.deferFocus();
25128                     }
25129                     return;
25130                 }
25131                 
25132                 if(k == e.ENTER){
25133                     r = this.doc.selection.createRange();
25134                     if(r){
25135                         var target = r.parentElement();
25136                         if(!target || target.tagName.toLowerCase() != 'li'){
25137                             e.stopEvent();
25138                             r.pasteHTML('<br />');
25139                             r.collapse(false);
25140                             r.select();
25141                         }
25142                     }
25143                 }
25144                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25145                     this.cleanUpPaste.defer(100, this);
25146                     return;
25147                 }
25148                 
25149                 
25150             };
25151         }else if(Roo.isOpera){
25152             return function(e){
25153                 var k = e.getKey();
25154                 if(k == e.TAB){
25155                     e.stopEvent();
25156                     this.win.focus();
25157                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25158                     this.deferFocus();
25159                 }
25160                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25161                     this.cleanUpPaste.defer(100, this);
25162                     return;
25163                 }
25164                 
25165             };
25166         }else if(Roo.isSafari){
25167             return function(e){
25168                 var k = e.getKey();
25169                 
25170                 if(k == e.TAB){
25171                     e.stopEvent();
25172                     this.execCmd('InsertText','\t');
25173                     this.deferFocus();
25174                     return;
25175                 }
25176                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25177                     this.cleanUpPaste.defer(100, this);
25178                     return;
25179                 }
25180                 
25181              };
25182         }
25183     }(),
25184     
25185     getAllAncestors: function()
25186     {
25187         var p = this.getSelectedNode();
25188         var a = [];
25189         if (!p) {
25190             a.push(p); // push blank onto stack..
25191             p = this.getParentElement();
25192         }
25193         
25194         
25195         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25196             a.push(p);
25197             p = p.parentNode;
25198         }
25199         a.push(this.doc.body);
25200         return a;
25201     },
25202     lastSel : false,
25203     lastSelNode : false,
25204     
25205     
25206     getSelection : function() 
25207     {
25208         this.assignDocWin();
25209         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25210     },
25211     
25212     getSelectedNode: function() 
25213     {
25214         // this may only work on Gecko!!!
25215         
25216         // should we cache this!!!!
25217         
25218         
25219         
25220          
25221         var range = this.createRange(this.getSelection()).cloneRange();
25222         
25223         if (Roo.isIE) {
25224             var parent = range.parentElement();
25225             while (true) {
25226                 var testRange = range.duplicate();
25227                 testRange.moveToElementText(parent);
25228                 if (testRange.inRange(range)) {
25229                     break;
25230                 }
25231                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25232                     break;
25233                 }
25234                 parent = parent.parentElement;
25235             }
25236             return parent;
25237         }
25238         
25239         // is ancestor a text element.
25240         var ac =  range.commonAncestorContainer;
25241         if (ac.nodeType == 3) {
25242             ac = ac.parentNode;
25243         }
25244         
25245         var ar = ac.childNodes;
25246          
25247         var nodes = [];
25248         var other_nodes = [];
25249         var has_other_nodes = false;
25250         for (var i=0;i<ar.length;i++) {
25251             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25252                 continue;
25253             }
25254             // fullly contained node.
25255             
25256             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25257                 nodes.push(ar[i]);
25258                 continue;
25259             }
25260             
25261             // probably selected..
25262             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25263                 other_nodes.push(ar[i]);
25264                 continue;
25265             }
25266             // outer..
25267             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25268                 continue;
25269             }
25270             
25271             
25272             has_other_nodes = true;
25273         }
25274         if (!nodes.length && other_nodes.length) {
25275             nodes= other_nodes;
25276         }
25277         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25278             return false;
25279         }
25280         
25281         return nodes[0];
25282     },
25283     createRange: function(sel)
25284     {
25285         // this has strange effects when using with 
25286         // top toolbar - not sure if it's a great idea.
25287         //this.editor.contentWindow.focus();
25288         if (typeof sel != "undefined") {
25289             try {
25290                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25291             } catch(e) {
25292                 return this.doc.createRange();
25293             }
25294         } else {
25295             return this.doc.createRange();
25296         }
25297     },
25298     getParentElement: function()
25299     {
25300         
25301         this.assignDocWin();
25302         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25303         
25304         var range = this.createRange(sel);
25305          
25306         try {
25307             var p = range.commonAncestorContainer;
25308             while (p.nodeType == 3) { // text node
25309                 p = p.parentNode;
25310             }
25311             return p;
25312         } catch (e) {
25313             return null;
25314         }
25315     
25316     },
25317     /***
25318      *
25319      * Range intersection.. the hard stuff...
25320      *  '-1' = before
25321      *  '0' = hits..
25322      *  '1' = after.
25323      *         [ -- selected range --- ]
25324      *   [fail]                        [fail]
25325      *
25326      *    basically..
25327      *      if end is before start or  hits it. fail.
25328      *      if start is after end or hits it fail.
25329      *
25330      *   if either hits (but other is outside. - then it's not 
25331      *   
25332      *    
25333      **/
25334     
25335     
25336     // @see http://www.thismuchiknow.co.uk/?p=64.
25337     rangeIntersectsNode : function(range, node)
25338     {
25339         var nodeRange = node.ownerDocument.createRange();
25340         try {
25341             nodeRange.selectNode(node);
25342         } catch (e) {
25343             nodeRange.selectNodeContents(node);
25344         }
25345     
25346         var rangeStartRange = range.cloneRange();
25347         rangeStartRange.collapse(true);
25348     
25349         var rangeEndRange = range.cloneRange();
25350         rangeEndRange.collapse(false);
25351     
25352         var nodeStartRange = nodeRange.cloneRange();
25353         nodeStartRange.collapse(true);
25354     
25355         var nodeEndRange = nodeRange.cloneRange();
25356         nodeEndRange.collapse(false);
25357     
25358         return rangeStartRange.compareBoundaryPoints(
25359                  Range.START_TO_START, nodeEndRange) == -1 &&
25360                rangeEndRange.compareBoundaryPoints(
25361                  Range.START_TO_START, nodeStartRange) == 1;
25362         
25363          
25364     },
25365     rangeCompareNode : function(range, node)
25366     {
25367         var nodeRange = node.ownerDocument.createRange();
25368         try {
25369             nodeRange.selectNode(node);
25370         } catch (e) {
25371             nodeRange.selectNodeContents(node);
25372         }
25373         
25374         
25375         range.collapse(true);
25376     
25377         nodeRange.collapse(true);
25378      
25379         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25380         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25381          
25382         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25383         
25384         var nodeIsBefore   =  ss == 1;
25385         var nodeIsAfter    = ee == -1;
25386         
25387         if (nodeIsBefore && nodeIsAfter) {
25388             return 0; // outer
25389         }
25390         if (!nodeIsBefore && nodeIsAfter) {
25391             return 1; //right trailed.
25392         }
25393         
25394         if (nodeIsBefore && !nodeIsAfter) {
25395             return 2;  // left trailed.
25396         }
25397         // fully contined.
25398         return 3;
25399     },
25400
25401     // private? - in a new class?
25402     cleanUpPaste :  function()
25403     {
25404         // cleans up the whole document..
25405         Roo.log('cleanuppaste');
25406         
25407         this.cleanUpChildren(this.doc.body);
25408         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25409         if (clean != this.doc.body.innerHTML) {
25410             this.doc.body.innerHTML = clean;
25411         }
25412         
25413     },
25414     
25415     cleanWordChars : function(input) {// change the chars to hex code
25416         var he = Roo.HtmlEditorCore;
25417         
25418         var output = input;
25419         Roo.each(he.swapCodes, function(sw) { 
25420             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25421             
25422             output = output.replace(swapper, sw[1]);
25423         });
25424         
25425         return output;
25426     },
25427     
25428     
25429     cleanUpChildren : function (n)
25430     {
25431         if (!n.childNodes.length) {
25432             return;
25433         }
25434         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25435            this.cleanUpChild(n.childNodes[i]);
25436         }
25437     },
25438     
25439     
25440         
25441     
25442     cleanUpChild : function (node)
25443     {
25444         var ed = this;
25445         //console.log(node);
25446         if (node.nodeName == "#text") {
25447             // clean up silly Windows -- stuff?
25448             return; 
25449         }
25450         if (node.nodeName == "#comment") {
25451             node.parentNode.removeChild(node);
25452             // clean up silly Windows -- stuff?
25453             return; 
25454         }
25455         var lcname = node.tagName.toLowerCase();
25456         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25457         // whitelist of tags..
25458         
25459         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25460             // remove node.
25461             node.parentNode.removeChild(node);
25462             return;
25463             
25464         }
25465         
25466         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25467         
25468         // spans with no attributes - just remove them..
25469         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25470             remove_keep_children = true;
25471         }
25472         
25473         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25474         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25475         
25476         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25477         //    remove_keep_children = true;
25478         //}
25479         
25480         if (remove_keep_children) {
25481             this.cleanUpChildren(node);
25482             // inserts everything just before this node...
25483             while (node.childNodes.length) {
25484                 var cn = node.childNodes[0];
25485                 node.removeChild(cn);
25486                 node.parentNode.insertBefore(cn, node);
25487             }
25488             node.parentNode.removeChild(node);
25489             return;
25490         }
25491         
25492         if (!node.attributes || !node.attributes.length) {
25493             
25494           
25495             
25496             
25497             this.cleanUpChildren(node);
25498             return;
25499         }
25500         
25501         function cleanAttr(n,v)
25502         {
25503             
25504             if (v.match(/^\./) || v.match(/^\//)) {
25505                 return;
25506             }
25507             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25508                 return;
25509             }
25510             if (v.match(/^#/)) {
25511                 return;
25512             }
25513             if (v.match(/^\{/)) { // allow template editing.
25514                 return;
25515             }
25516 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25517             node.removeAttribute(n);
25518             
25519         }
25520         
25521         var cwhite = this.cwhite;
25522         var cblack = this.cblack;
25523             
25524         function cleanStyle(n,v)
25525         {
25526             if (v.match(/expression/)) { //XSS?? should we even bother..
25527                 node.removeAttribute(n);
25528                 return;
25529             }
25530             
25531             var parts = v.split(/;/);
25532             var clean = [];
25533             
25534             Roo.each(parts, function(p) {
25535                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25536                 if (!p.length) {
25537                     return true;
25538                 }
25539                 var l = p.split(':').shift().replace(/\s+/g,'');
25540                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25541                 
25542                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25543 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25544                     //node.removeAttribute(n);
25545                     return true;
25546                 }
25547                 //Roo.log()
25548                 // only allow 'c whitelisted system attributes'
25549                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25550 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25551                     //node.removeAttribute(n);
25552                     return true;
25553                 }
25554                 
25555                 
25556                  
25557                 
25558                 clean.push(p);
25559                 return true;
25560             });
25561             if (clean.length) { 
25562                 node.setAttribute(n, clean.join(';'));
25563             } else {
25564                 node.removeAttribute(n);
25565             }
25566             
25567         }
25568         
25569         
25570         for (var i = node.attributes.length-1; i > -1 ; i--) {
25571             var a = node.attributes[i];
25572             //console.log(a);
25573             
25574             if (a.name.toLowerCase().substr(0,2)=='on')  {
25575                 node.removeAttribute(a.name);
25576                 continue;
25577             }
25578             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25579                 node.removeAttribute(a.name);
25580                 continue;
25581             }
25582             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25583                 cleanAttr(a.name,a.value); // fixme..
25584                 continue;
25585             }
25586             if (a.name == 'style') {
25587                 cleanStyle(a.name,a.value);
25588                 continue;
25589             }
25590             /// clean up MS crap..
25591             // tecnically this should be a list of valid class'es..
25592             
25593             
25594             if (a.name == 'class') {
25595                 if (a.value.match(/^Mso/)) {
25596                     node.removeAttribute('class');
25597                 }
25598                 
25599                 if (a.value.match(/^body$/)) {
25600                     node.removeAttribute('class');
25601                 }
25602                 continue;
25603             }
25604             
25605             // style cleanup!?
25606             // class cleanup?
25607             
25608         }
25609         
25610         
25611         this.cleanUpChildren(node);
25612         
25613         
25614     },
25615     
25616     /**
25617      * Clean up MS wordisms...
25618      */
25619     cleanWord : function(node)
25620     {
25621         if (!node) {
25622             this.cleanWord(this.doc.body);
25623             return;
25624         }
25625         
25626         if(
25627                 node.nodeName == 'SPAN' &&
25628                 !node.hasAttributes() &&
25629                 node.childNodes.length == 1 &&
25630                 node.firstChild.nodeName == "#text"  
25631         ) {
25632             var textNode = node.firstChild;
25633             node.removeChild(textNode);
25634             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25635                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25636             }
25637             node.parentNode.insertBefore(textNode, node);
25638             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25639                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25640             }
25641             node.parentNode.removeChild(node);
25642         }
25643         
25644         if (node.nodeName == "#text") {
25645             // clean up silly Windows -- stuff?
25646             return; 
25647         }
25648         if (node.nodeName == "#comment") {
25649             node.parentNode.removeChild(node);
25650             // clean up silly Windows -- stuff?
25651             return; 
25652         }
25653         
25654         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25655             node.parentNode.removeChild(node);
25656             return;
25657         }
25658         //Roo.log(node.tagName);
25659         // remove - but keep children..
25660         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25661             //Roo.log('-- removed');
25662             while (node.childNodes.length) {
25663                 var cn = node.childNodes[0];
25664                 node.removeChild(cn);
25665                 node.parentNode.insertBefore(cn, node);
25666                 // move node to parent - and clean it..
25667                 this.cleanWord(cn);
25668             }
25669             node.parentNode.removeChild(node);
25670             /// no need to iterate chidlren = it's got none..
25671             //this.iterateChildren(node, this.cleanWord);
25672             return;
25673         }
25674         // clean styles
25675         if (node.className.length) {
25676             
25677             var cn = node.className.split(/\W+/);
25678             var cna = [];
25679             Roo.each(cn, function(cls) {
25680                 if (cls.match(/Mso[a-zA-Z]+/)) {
25681                     return;
25682                 }
25683                 cna.push(cls);
25684             });
25685             node.className = cna.length ? cna.join(' ') : '';
25686             if (!cna.length) {
25687                 node.removeAttribute("class");
25688             }
25689         }
25690         
25691         if (node.hasAttribute("lang")) {
25692             node.removeAttribute("lang");
25693         }
25694         
25695         if (node.hasAttribute("style")) {
25696             
25697             var styles = node.getAttribute("style").split(";");
25698             var nstyle = [];
25699             Roo.each(styles, function(s) {
25700                 if (!s.match(/:/)) {
25701                     return;
25702                 }
25703                 var kv = s.split(":");
25704                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25705                     return;
25706                 }
25707                 // what ever is left... we allow.
25708                 nstyle.push(s);
25709             });
25710             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25711             if (!nstyle.length) {
25712                 node.removeAttribute('style');
25713             }
25714         }
25715         this.iterateChildren(node, this.cleanWord);
25716         
25717         
25718         
25719     },
25720     /**
25721      * iterateChildren of a Node, calling fn each time, using this as the scole..
25722      * @param {DomNode} node node to iterate children of.
25723      * @param {Function} fn method of this class to call on each item.
25724      */
25725     iterateChildren : function(node, fn)
25726     {
25727         if (!node.childNodes.length) {
25728                 return;
25729         }
25730         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25731            fn.call(this, node.childNodes[i])
25732         }
25733     },
25734     
25735     
25736     /**
25737      * cleanTableWidths.
25738      *
25739      * Quite often pasting from word etc.. results in tables with column and widths.
25740      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25741      *
25742      */
25743     cleanTableWidths : function(node)
25744     {
25745          
25746          
25747         if (!node) {
25748             this.cleanTableWidths(this.doc.body);
25749             return;
25750         }
25751         
25752         // ignore list...
25753         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25754             return; 
25755         }
25756         Roo.log(node.tagName);
25757         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25758             this.iterateChildren(node, this.cleanTableWidths);
25759             return;
25760         }
25761         if (node.hasAttribute('width')) {
25762             node.removeAttribute('width');
25763         }
25764         
25765          
25766         if (node.hasAttribute("style")) {
25767             // pretty basic...
25768             
25769             var styles = node.getAttribute("style").split(";");
25770             var nstyle = [];
25771             Roo.each(styles, function(s) {
25772                 if (!s.match(/:/)) {
25773                     return;
25774                 }
25775                 var kv = s.split(":");
25776                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25777                     return;
25778                 }
25779                 // what ever is left... we allow.
25780                 nstyle.push(s);
25781             });
25782             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25783             if (!nstyle.length) {
25784                 node.removeAttribute('style');
25785             }
25786         }
25787         
25788         this.iterateChildren(node, this.cleanTableWidths);
25789         
25790         
25791     },
25792     
25793     
25794     
25795     
25796     domToHTML : function(currentElement, depth, nopadtext) {
25797         
25798         depth = depth || 0;
25799         nopadtext = nopadtext || false;
25800     
25801         if (!currentElement) {
25802             return this.domToHTML(this.doc.body);
25803         }
25804         
25805         //Roo.log(currentElement);
25806         var j;
25807         var allText = false;
25808         var nodeName = currentElement.nodeName;
25809         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25810         
25811         if  (nodeName == '#text') {
25812             
25813             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25814         }
25815         
25816         
25817         var ret = '';
25818         if (nodeName != 'BODY') {
25819              
25820             var i = 0;
25821             // Prints the node tagName, such as <A>, <IMG>, etc
25822             if (tagName) {
25823                 var attr = [];
25824                 for(i = 0; i < currentElement.attributes.length;i++) {
25825                     // quoting?
25826                     var aname = currentElement.attributes.item(i).name;
25827                     if (!currentElement.attributes.item(i).value.length) {
25828                         continue;
25829                     }
25830                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25831                 }
25832                 
25833                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25834             } 
25835             else {
25836                 
25837                 // eack
25838             }
25839         } else {
25840             tagName = false;
25841         }
25842         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25843             return ret;
25844         }
25845         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25846             nopadtext = true;
25847         }
25848         
25849         
25850         // Traverse the tree
25851         i = 0;
25852         var currentElementChild = currentElement.childNodes.item(i);
25853         var allText = true;
25854         var innerHTML  = '';
25855         lastnode = '';
25856         while (currentElementChild) {
25857             // Formatting code (indent the tree so it looks nice on the screen)
25858             var nopad = nopadtext;
25859             if (lastnode == 'SPAN') {
25860                 nopad  = true;
25861             }
25862             // text
25863             if  (currentElementChild.nodeName == '#text') {
25864                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25865                 toadd = nopadtext ? toadd : toadd.trim();
25866                 if (!nopad && toadd.length > 80) {
25867                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25868                 }
25869                 innerHTML  += toadd;
25870                 
25871                 i++;
25872                 currentElementChild = currentElement.childNodes.item(i);
25873                 lastNode = '';
25874                 continue;
25875             }
25876             allText = false;
25877             
25878             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25879                 
25880             // Recursively traverse the tree structure of the child node
25881             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25882             lastnode = currentElementChild.nodeName;
25883             i++;
25884             currentElementChild=currentElement.childNodes.item(i);
25885         }
25886         
25887         ret += innerHTML;
25888         
25889         if (!allText) {
25890                 // The remaining code is mostly for formatting the tree
25891             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25892         }
25893         
25894         
25895         if (tagName) {
25896             ret+= "</"+tagName+">";
25897         }
25898         return ret;
25899         
25900     },
25901         
25902     applyBlacklists : function()
25903     {
25904         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25905         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25906         
25907         this.white = [];
25908         this.black = [];
25909         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25910             if (b.indexOf(tag) > -1) {
25911                 return;
25912             }
25913             this.white.push(tag);
25914             
25915         }, this);
25916         
25917         Roo.each(w, function(tag) {
25918             if (b.indexOf(tag) > -1) {
25919                 return;
25920             }
25921             if (this.white.indexOf(tag) > -1) {
25922                 return;
25923             }
25924             this.white.push(tag);
25925             
25926         }, this);
25927         
25928         
25929         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25930             if (w.indexOf(tag) > -1) {
25931                 return;
25932             }
25933             this.black.push(tag);
25934             
25935         }, this);
25936         
25937         Roo.each(b, function(tag) {
25938             if (w.indexOf(tag) > -1) {
25939                 return;
25940             }
25941             if (this.black.indexOf(tag) > -1) {
25942                 return;
25943             }
25944             this.black.push(tag);
25945             
25946         }, this);
25947         
25948         
25949         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25950         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25951         
25952         this.cwhite = [];
25953         this.cblack = [];
25954         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25955             if (b.indexOf(tag) > -1) {
25956                 return;
25957             }
25958             this.cwhite.push(tag);
25959             
25960         }, this);
25961         
25962         Roo.each(w, function(tag) {
25963             if (b.indexOf(tag) > -1) {
25964                 return;
25965             }
25966             if (this.cwhite.indexOf(tag) > -1) {
25967                 return;
25968             }
25969             this.cwhite.push(tag);
25970             
25971         }, this);
25972         
25973         
25974         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25975             if (w.indexOf(tag) > -1) {
25976                 return;
25977             }
25978             this.cblack.push(tag);
25979             
25980         }, this);
25981         
25982         Roo.each(b, function(tag) {
25983             if (w.indexOf(tag) > -1) {
25984                 return;
25985             }
25986             if (this.cblack.indexOf(tag) > -1) {
25987                 return;
25988             }
25989             this.cblack.push(tag);
25990             
25991         }, this);
25992     },
25993     
25994     setStylesheets : function(stylesheets)
25995     {
25996         if(typeof(stylesheets) == 'string'){
25997             Roo.get(this.iframe.contentDocument.head).createChild({
25998                 tag : 'link',
25999                 rel : 'stylesheet',
26000                 type : 'text/css',
26001                 href : stylesheets
26002             });
26003             
26004             return;
26005         }
26006         var _this = this;
26007      
26008         Roo.each(stylesheets, function(s) {
26009             if(!s.length){
26010                 return;
26011             }
26012             
26013             Roo.get(_this.iframe.contentDocument.head).createChild({
26014                 tag : 'link',
26015                 rel : 'stylesheet',
26016                 type : 'text/css',
26017                 href : s
26018             });
26019         });
26020
26021         
26022     },
26023     
26024     removeStylesheets : function()
26025     {
26026         var _this = this;
26027         
26028         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26029             s.remove();
26030         });
26031     },
26032     
26033     setStyle : function(style)
26034     {
26035         Roo.get(this.iframe.contentDocument.head).createChild({
26036             tag : 'style',
26037             type : 'text/css',
26038             html : style
26039         });
26040
26041         return;
26042     }
26043     
26044     // hide stuff that is not compatible
26045     /**
26046      * @event blur
26047      * @hide
26048      */
26049     /**
26050      * @event change
26051      * @hide
26052      */
26053     /**
26054      * @event focus
26055      * @hide
26056      */
26057     /**
26058      * @event specialkey
26059      * @hide
26060      */
26061     /**
26062      * @cfg {String} fieldClass @hide
26063      */
26064     /**
26065      * @cfg {String} focusClass @hide
26066      */
26067     /**
26068      * @cfg {String} autoCreate @hide
26069      */
26070     /**
26071      * @cfg {String} inputType @hide
26072      */
26073     /**
26074      * @cfg {String} invalidClass @hide
26075      */
26076     /**
26077      * @cfg {String} invalidText @hide
26078      */
26079     /**
26080      * @cfg {String} msgFx @hide
26081      */
26082     /**
26083      * @cfg {String} validateOnBlur @hide
26084      */
26085 });
26086
26087 Roo.HtmlEditorCore.white = [
26088         'area', 'br', 'img', 'input', 'hr', 'wbr',
26089         
26090        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26091        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26092        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26093        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26094        'table',   'ul',         'xmp', 
26095        
26096        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26097       'thead',   'tr', 
26098      
26099       'dir', 'menu', 'ol', 'ul', 'dl',
26100        
26101       'embed',  'object'
26102 ];
26103
26104
26105 Roo.HtmlEditorCore.black = [
26106     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26107         'applet', // 
26108         'base',   'basefont', 'bgsound', 'blink',  'body', 
26109         'frame',  'frameset', 'head',    'html',   'ilayer', 
26110         'iframe', 'layer',  'link',     'meta',    'object',   
26111         'script', 'style' ,'title',  'xml' // clean later..
26112 ];
26113 Roo.HtmlEditorCore.clean = [
26114     'script', 'style', 'title', 'xml'
26115 ];
26116 Roo.HtmlEditorCore.remove = [
26117     'font'
26118 ];
26119 // attributes..
26120
26121 Roo.HtmlEditorCore.ablack = [
26122     'on'
26123 ];
26124     
26125 Roo.HtmlEditorCore.aclean = [ 
26126     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26127 ];
26128
26129 // protocols..
26130 Roo.HtmlEditorCore.pwhite= [
26131         'http',  'https',  'mailto'
26132 ];
26133
26134 // white listed style attributes.
26135 Roo.HtmlEditorCore.cwhite= [
26136       //  'text-align', /// default is to allow most things..
26137       
26138          
26139 //        'font-size'//??
26140 ];
26141
26142 // black listed style attributes.
26143 Roo.HtmlEditorCore.cblack= [
26144       //  'font-size' -- this can be set by the project 
26145 ];
26146
26147
26148 Roo.HtmlEditorCore.swapCodes   =[ 
26149     [    8211, "&#8211;" ], 
26150     [    8212, "&#8212;" ], 
26151     [    8216,  "'" ],  
26152     [    8217, "'" ],  
26153     [    8220, '"' ],  
26154     [    8221, '"' ],  
26155     [    8226, "*" ],  
26156     [    8230, "..." ]
26157 ]; 
26158
26159     /*
26160  * - LGPL
26161  *
26162  * HtmlEditor
26163  * 
26164  */
26165
26166 /**
26167  * @class Roo.bootstrap.HtmlEditor
26168  * @extends Roo.bootstrap.TextArea
26169  * Bootstrap HtmlEditor class
26170
26171  * @constructor
26172  * Create a new HtmlEditor
26173  * @param {Object} config The config object
26174  */
26175
26176 Roo.bootstrap.HtmlEditor = function(config){
26177     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26178     if (!this.toolbars) {
26179         this.toolbars = [];
26180     }
26181     
26182     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26183     this.addEvents({
26184             /**
26185              * @event initialize
26186              * Fires when the editor is fully initialized (including the iframe)
26187              * @param {HtmlEditor} this
26188              */
26189             initialize: true,
26190             /**
26191              * @event activate
26192              * Fires when the editor is first receives the focus. Any insertion must wait
26193              * until after this event.
26194              * @param {HtmlEditor} this
26195              */
26196             activate: true,
26197              /**
26198              * @event beforesync
26199              * Fires before the textarea is updated with content from the editor iframe. Return false
26200              * to cancel the sync.
26201              * @param {HtmlEditor} this
26202              * @param {String} html
26203              */
26204             beforesync: true,
26205              /**
26206              * @event beforepush
26207              * Fires before the iframe editor is updated with content from the textarea. Return false
26208              * to cancel the push.
26209              * @param {HtmlEditor} this
26210              * @param {String} html
26211              */
26212             beforepush: true,
26213              /**
26214              * @event sync
26215              * Fires when the textarea is updated with content from the editor iframe.
26216              * @param {HtmlEditor} this
26217              * @param {String} html
26218              */
26219             sync: true,
26220              /**
26221              * @event push
26222              * Fires when the iframe editor is updated with content from the textarea.
26223              * @param {HtmlEditor} this
26224              * @param {String} html
26225              */
26226             push: true,
26227              /**
26228              * @event editmodechange
26229              * Fires when the editor switches edit modes
26230              * @param {HtmlEditor} this
26231              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26232              */
26233             editmodechange: true,
26234             /**
26235              * @event editorevent
26236              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26237              * @param {HtmlEditor} this
26238              */
26239             editorevent: true,
26240             /**
26241              * @event firstfocus
26242              * Fires when on first focus - needed by toolbars..
26243              * @param {HtmlEditor} this
26244              */
26245             firstfocus: true,
26246             /**
26247              * @event autosave
26248              * Auto save the htmlEditor value as a file into Events
26249              * @param {HtmlEditor} this
26250              */
26251             autosave: true,
26252             /**
26253              * @event savedpreview
26254              * preview the saved version of htmlEditor
26255              * @param {HtmlEditor} this
26256              */
26257             savedpreview: true
26258         });
26259 };
26260
26261
26262 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26263     
26264     
26265       /**
26266      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26267      */
26268     toolbars : false,
26269     
26270      /**
26271     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26272     */
26273     btns : [],
26274    
26275      /**
26276      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26277      *                        Roo.resizable.
26278      */
26279     resizable : false,
26280      /**
26281      * @cfg {Number} height (in pixels)
26282      */   
26283     height: 300,
26284    /**
26285      * @cfg {Number} width (in pixels)
26286      */   
26287     width: false,
26288     
26289     /**
26290      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26291      * 
26292      */
26293     stylesheets: false,
26294     
26295     // id of frame..
26296     frameId: false,
26297     
26298     // private properties
26299     validationEvent : false,
26300     deferHeight: true,
26301     initialized : false,
26302     activated : false,
26303     
26304     onFocus : Roo.emptyFn,
26305     iframePad:3,
26306     hideMode:'offsets',
26307     
26308     tbContainer : false,
26309     
26310     bodyCls : '',
26311     
26312     toolbarContainer :function() {
26313         return this.wrap.select('.x-html-editor-tb',true).first();
26314     },
26315
26316     /**
26317      * Protected method that will not generally be called directly. It
26318      * is called when the editor creates its toolbar. Override this method if you need to
26319      * add custom toolbar buttons.
26320      * @param {HtmlEditor} editor
26321      */
26322     createToolbar : function(){
26323         Roo.log('renewing');
26324         Roo.log("create toolbars");
26325         
26326         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26327         this.toolbars[0].render(this.toolbarContainer());
26328         
26329         return;
26330         
26331 //        if (!editor.toolbars || !editor.toolbars.length) {
26332 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26333 //        }
26334 //        
26335 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26336 //            editor.toolbars[i] = Roo.factory(
26337 //                    typeof(editor.toolbars[i]) == 'string' ?
26338 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26339 //                Roo.bootstrap.HtmlEditor);
26340 //            editor.toolbars[i].init(editor);
26341 //        }
26342     },
26343
26344      
26345     // private
26346     onRender : function(ct, position)
26347     {
26348        // Roo.log("Call onRender: " + this.xtype);
26349         var _t = this;
26350         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26351       
26352         this.wrap = this.inputEl().wrap({
26353             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26354         });
26355         
26356         this.editorcore.onRender(ct, position);
26357          
26358         if (this.resizable) {
26359             this.resizeEl = new Roo.Resizable(this.wrap, {
26360                 pinned : true,
26361                 wrap: true,
26362                 dynamic : true,
26363                 minHeight : this.height,
26364                 height: this.height,
26365                 handles : this.resizable,
26366                 width: this.width,
26367                 listeners : {
26368                     resize : function(r, w, h) {
26369                         _t.onResize(w,h); // -something
26370                     }
26371                 }
26372             });
26373             
26374         }
26375         this.createToolbar(this);
26376        
26377         
26378         if(!this.width && this.resizable){
26379             this.setSize(this.wrap.getSize());
26380         }
26381         if (this.resizeEl) {
26382             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26383             // should trigger onReize..
26384         }
26385         
26386     },
26387
26388     // private
26389     onResize : function(w, h)
26390     {
26391         Roo.log('resize: ' +w + ',' + h );
26392         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26393         var ew = false;
26394         var eh = false;
26395         
26396         if(this.inputEl() ){
26397             if(typeof w == 'number'){
26398                 var aw = w - this.wrap.getFrameWidth('lr');
26399                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26400                 ew = aw;
26401             }
26402             if(typeof h == 'number'){
26403                  var tbh = -11;  // fixme it needs to tool bar size!
26404                 for (var i =0; i < this.toolbars.length;i++) {
26405                     // fixme - ask toolbars for heights?
26406                     tbh += this.toolbars[i].el.getHeight();
26407                     //if (this.toolbars[i].footer) {
26408                     //    tbh += this.toolbars[i].footer.el.getHeight();
26409                     //}
26410                 }
26411               
26412                 
26413                 
26414                 
26415                 
26416                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26417                 ah -= 5; // knock a few pixes off for look..
26418                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26419                 var eh = ah;
26420             }
26421         }
26422         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26423         this.editorcore.onResize(ew,eh);
26424         
26425     },
26426
26427     /**
26428      * Toggles the editor between standard and source edit mode.
26429      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26430      */
26431     toggleSourceEdit : function(sourceEditMode)
26432     {
26433         this.editorcore.toggleSourceEdit(sourceEditMode);
26434         
26435         if(this.editorcore.sourceEditMode){
26436             Roo.log('editor - showing textarea');
26437             
26438 //            Roo.log('in');
26439 //            Roo.log(this.syncValue());
26440             this.syncValue();
26441             this.inputEl().removeClass(['hide', 'x-hidden']);
26442             this.inputEl().dom.removeAttribute('tabIndex');
26443             this.inputEl().focus();
26444         }else{
26445             Roo.log('editor - hiding textarea');
26446 //            Roo.log('out')
26447 //            Roo.log(this.pushValue()); 
26448             this.pushValue();
26449             
26450             this.inputEl().addClass(['hide', 'x-hidden']);
26451             this.inputEl().dom.setAttribute('tabIndex', -1);
26452             //this.deferFocus();
26453         }
26454          
26455         if(this.resizable){
26456             this.setSize(this.wrap.getSize());
26457         }
26458         
26459         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26460     },
26461  
26462     // private (for BoxComponent)
26463     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26464
26465     // private (for BoxComponent)
26466     getResizeEl : function(){
26467         return this.wrap;
26468     },
26469
26470     // private (for BoxComponent)
26471     getPositionEl : function(){
26472         return this.wrap;
26473     },
26474
26475     // private
26476     initEvents : function(){
26477         this.originalValue = this.getValue();
26478     },
26479
26480 //    /**
26481 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26482 //     * @method
26483 //     */
26484 //    markInvalid : Roo.emptyFn,
26485 //    /**
26486 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26487 //     * @method
26488 //     */
26489 //    clearInvalid : Roo.emptyFn,
26490
26491     setValue : function(v){
26492         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26493         this.editorcore.pushValue();
26494     },
26495
26496      
26497     // private
26498     deferFocus : function(){
26499         this.focus.defer(10, this);
26500     },
26501
26502     // doc'ed in Field
26503     focus : function(){
26504         this.editorcore.focus();
26505         
26506     },
26507       
26508
26509     // private
26510     onDestroy : function(){
26511         
26512         
26513         
26514         if(this.rendered){
26515             
26516             for (var i =0; i < this.toolbars.length;i++) {
26517                 // fixme - ask toolbars for heights?
26518                 this.toolbars[i].onDestroy();
26519             }
26520             
26521             this.wrap.dom.innerHTML = '';
26522             this.wrap.remove();
26523         }
26524     },
26525
26526     // private
26527     onFirstFocus : function(){
26528         //Roo.log("onFirstFocus");
26529         this.editorcore.onFirstFocus();
26530          for (var i =0; i < this.toolbars.length;i++) {
26531             this.toolbars[i].onFirstFocus();
26532         }
26533         
26534     },
26535     
26536     // private
26537     syncValue : function()
26538     {   
26539         this.editorcore.syncValue();
26540     },
26541     
26542     pushValue : function()
26543     {   
26544         this.editorcore.pushValue();
26545     }
26546      
26547     
26548     // hide stuff that is not compatible
26549     /**
26550      * @event blur
26551      * @hide
26552      */
26553     /**
26554      * @event change
26555      * @hide
26556      */
26557     /**
26558      * @event focus
26559      * @hide
26560      */
26561     /**
26562      * @event specialkey
26563      * @hide
26564      */
26565     /**
26566      * @cfg {String} fieldClass @hide
26567      */
26568     /**
26569      * @cfg {String} focusClass @hide
26570      */
26571     /**
26572      * @cfg {String} autoCreate @hide
26573      */
26574     /**
26575      * @cfg {String} inputType @hide
26576      */
26577      
26578     /**
26579      * @cfg {String} invalidText @hide
26580      */
26581     /**
26582      * @cfg {String} msgFx @hide
26583      */
26584     /**
26585      * @cfg {String} validateOnBlur @hide
26586      */
26587 });
26588  
26589     
26590    
26591    
26592    
26593       
26594 Roo.namespace('Roo.bootstrap.htmleditor');
26595 /**
26596  * @class Roo.bootstrap.HtmlEditorToolbar1
26597  * Basic Toolbar
26598  * 
26599  * @example
26600  * Usage:
26601  *
26602  new Roo.bootstrap.HtmlEditor({
26603     ....
26604     toolbars : [
26605         new Roo.bootstrap.HtmlEditorToolbar1({
26606             disable : { fonts: 1 , format: 1, ..., ... , ...],
26607             btns : [ .... ]
26608         })
26609     }
26610      
26611  * 
26612  * @cfg {Object} disable List of elements to disable..
26613  * @cfg {Array} btns List of additional buttons.
26614  * 
26615  * 
26616  * NEEDS Extra CSS? 
26617  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26618  */
26619  
26620 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26621 {
26622     
26623     Roo.apply(this, config);
26624     
26625     // default disabled, based on 'good practice'..
26626     this.disable = this.disable || {};
26627     Roo.applyIf(this.disable, {
26628         fontSize : true,
26629         colors : true,
26630         specialElements : true
26631     });
26632     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26633     
26634     this.editor = config.editor;
26635     this.editorcore = config.editor.editorcore;
26636     
26637     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26638     
26639     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26640     // dont call parent... till later.
26641 }
26642 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26643      
26644     bar : true,
26645     
26646     editor : false,
26647     editorcore : false,
26648     
26649     
26650     formats : [
26651         "p" ,  
26652         "h1","h2","h3","h4","h5","h6", 
26653         "pre", "code", 
26654         "abbr", "acronym", "address", "cite", "samp", "var",
26655         'div','span'
26656     ],
26657     
26658     onRender : function(ct, position)
26659     {
26660        // Roo.log("Call onRender: " + this.xtype);
26661         
26662        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26663        Roo.log(this.el);
26664        this.el.dom.style.marginBottom = '0';
26665        var _this = this;
26666        var editorcore = this.editorcore;
26667        var editor= this.editor;
26668        
26669        var children = [];
26670        var btn = function(id,cmd , toggle, handler, html){
26671        
26672             var  event = toggle ? 'toggle' : 'click';
26673        
26674             var a = {
26675                 size : 'sm',
26676                 xtype: 'Button',
26677                 xns: Roo.bootstrap,
26678                 //glyphicon : id,
26679                 fa: id,
26680                 cmd : id || cmd,
26681                 enableToggle:toggle !== false,
26682                 html : html || '',
26683                 pressed : toggle ? false : null,
26684                 listeners : {}
26685             };
26686             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26687                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26688             };
26689             children.push(a);
26690             return a;
26691        }
26692        
26693     //    var cb_box = function...
26694         
26695         var style = {
26696                 xtype: 'Button',
26697                 size : 'sm',
26698                 xns: Roo.bootstrap,
26699                 fa : 'font',
26700                 //html : 'submit'
26701                 menu : {
26702                     xtype: 'Menu',
26703                     xns: Roo.bootstrap,
26704                     items:  []
26705                 }
26706         };
26707         Roo.each(this.formats, function(f) {
26708             style.menu.items.push({
26709                 xtype :'MenuItem',
26710                 xns: Roo.bootstrap,
26711                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26712                 tagname : f,
26713                 listeners : {
26714                     click : function()
26715                     {
26716                         editorcore.insertTag(this.tagname);
26717                         editor.focus();
26718                     }
26719                 }
26720                 
26721             });
26722         });
26723         children.push(style);   
26724         
26725         btn('bold',false,true);
26726         btn('italic',false,true);
26727         btn('align-left', 'justifyleft',true);
26728         btn('align-center', 'justifycenter',true);
26729         btn('align-right' , 'justifyright',true);
26730         btn('link', false, false, function(btn) {
26731             //Roo.log("create link?");
26732             var url = prompt(this.createLinkText, this.defaultLinkValue);
26733             if(url && url != 'http:/'+'/'){
26734                 this.editorcore.relayCmd('createlink', url);
26735             }
26736         }),
26737         btn('list','insertunorderedlist',true);
26738         btn('pencil', false,true, function(btn){
26739                 Roo.log(this);
26740                 this.toggleSourceEdit(btn.pressed);
26741         });
26742         
26743         if (this.editor.btns.length > 0) {
26744             for (var i = 0; i<this.editor.btns.length; i++) {
26745                 children.push(this.editor.btns[i]);
26746             }
26747         }
26748         
26749         /*
26750         var cog = {
26751                 xtype: 'Button',
26752                 size : 'sm',
26753                 xns: Roo.bootstrap,
26754                 glyphicon : 'cog',
26755                 //html : 'submit'
26756                 menu : {
26757                     xtype: 'Menu',
26758                     xns: Roo.bootstrap,
26759                     items:  []
26760                 }
26761         };
26762         
26763         cog.menu.items.push({
26764             xtype :'MenuItem',
26765             xns: Roo.bootstrap,
26766             html : Clean styles,
26767             tagname : f,
26768             listeners : {
26769                 click : function()
26770                 {
26771                     editorcore.insertTag(this.tagname);
26772                     editor.focus();
26773                 }
26774             }
26775             
26776         });
26777        */
26778         
26779          
26780        this.xtype = 'NavSimplebar';
26781         
26782         for(var i=0;i< children.length;i++) {
26783             
26784             this.buttons.add(this.addxtypeChild(children[i]));
26785             
26786         }
26787         
26788         editor.on('editorevent', this.updateToolbar, this);
26789     },
26790     onBtnClick : function(id)
26791     {
26792        this.editorcore.relayCmd(id);
26793        this.editorcore.focus();
26794     },
26795     
26796     /**
26797      * Protected method that will not generally be called directly. It triggers
26798      * a toolbar update by reading the markup state of the current selection in the editor.
26799      */
26800     updateToolbar: function(){
26801
26802         if(!this.editorcore.activated){
26803             this.editor.onFirstFocus(); // is this neeed?
26804             return;
26805         }
26806
26807         var btns = this.buttons; 
26808         var doc = this.editorcore.doc;
26809         btns.get('bold').setActive(doc.queryCommandState('bold'));
26810         btns.get('italic').setActive(doc.queryCommandState('italic'));
26811         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26812         
26813         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26814         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26815         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26816         
26817         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26818         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26819          /*
26820         
26821         var ans = this.editorcore.getAllAncestors();
26822         if (this.formatCombo) {
26823             
26824             
26825             var store = this.formatCombo.store;
26826             this.formatCombo.setValue("");
26827             for (var i =0; i < ans.length;i++) {
26828                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26829                     // select it..
26830                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26831                     break;
26832                 }
26833             }
26834         }
26835         
26836         
26837         
26838         // hides menus... - so this cant be on a menu...
26839         Roo.bootstrap.MenuMgr.hideAll();
26840         */
26841         Roo.bootstrap.MenuMgr.hideAll();
26842         //this.editorsyncValue();
26843     },
26844     onFirstFocus: function() {
26845         this.buttons.each(function(item){
26846            item.enable();
26847         });
26848     },
26849     toggleSourceEdit : function(sourceEditMode){
26850         
26851           
26852         if(sourceEditMode){
26853             Roo.log("disabling buttons");
26854            this.buttons.each( function(item){
26855                 if(item.cmd != 'pencil'){
26856                     item.disable();
26857                 }
26858             });
26859           
26860         }else{
26861             Roo.log("enabling buttons");
26862             if(this.editorcore.initialized){
26863                 this.buttons.each( function(item){
26864                     item.enable();
26865                 });
26866             }
26867             
26868         }
26869         Roo.log("calling toggole on editor");
26870         // tell the editor that it's been pressed..
26871         this.editor.toggleSourceEdit(sourceEditMode);
26872        
26873     }
26874 });
26875
26876
26877
26878
26879  
26880 /*
26881  * - LGPL
26882  */
26883
26884 /**
26885  * @class Roo.bootstrap.Markdown
26886  * @extends Roo.bootstrap.TextArea
26887  * Bootstrap Showdown editable area
26888  * @cfg {string} content
26889  * 
26890  * @constructor
26891  * Create a new Showdown
26892  */
26893
26894 Roo.bootstrap.Markdown = function(config){
26895     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26896    
26897 };
26898
26899 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26900     
26901     editing :false,
26902     
26903     initEvents : function()
26904     {
26905         
26906         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26907         this.markdownEl = this.el.createChild({
26908             cls : 'roo-markdown-area'
26909         });
26910         this.inputEl().addClass('d-none');
26911         if (this.getValue() == '') {
26912             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26913             
26914         } else {
26915             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26916         }
26917         this.markdownEl.on('click', this.toggleTextEdit, this);
26918         this.on('blur', this.toggleTextEdit, this);
26919         this.on('specialkey', this.resizeTextArea, this);
26920     },
26921     
26922     toggleTextEdit : function()
26923     {
26924         var sh = this.markdownEl.getHeight();
26925         this.inputEl().addClass('d-none');
26926         this.markdownEl.addClass('d-none');
26927         if (!this.editing) {
26928             // show editor?
26929             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26930             this.inputEl().removeClass('d-none');
26931             this.inputEl().focus();
26932             this.editing = true;
26933             return;
26934         }
26935         // show showdown...
26936         this.updateMarkdown();
26937         this.markdownEl.removeClass('d-none');
26938         this.editing = false;
26939         return;
26940     },
26941     updateMarkdown : function()
26942     {
26943         if (this.getValue() == '') {
26944             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26945             return;
26946         }
26947  
26948         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26949     },
26950     
26951     resizeTextArea: function () {
26952         
26953         var sh = 100;
26954         Roo.log([sh, this.getValue().split("\n").length * 30]);
26955         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26956     },
26957     setValue : function(val)
26958     {
26959         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26960         if (!this.editing) {
26961             this.updateMarkdown();
26962         }
26963         
26964     },
26965     focus : function()
26966     {
26967         if (!this.editing) {
26968             this.toggleTextEdit();
26969         }
26970         
26971     }
26972
26973
26974 });
26975 /**
26976  * @class Roo.bootstrap.Table.AbstractSelectionModel
26977  * @extends Roo.util.Observable
26978  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26979  * implemented by descendant classes.  This class should not be directly instantiated.
26980  * @constructor
26981  */
26982 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26983     this.locked = false;
26984     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26985 };
26986
26987
26988 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26989     /** @ignore Called by the grid automatically. Do not call directly. */
26990     init : function(grid){
26991         this.grid = grid;
26992         this.initEvents();
26993     },
26994
26995     /**
26996      * Locks the selections.
26997      */
26998     lock : function(){
26999         this.locked = true;
27000     },
27001
27002     /**
27003      * Unlocks the selections.
27004      */
27005     unlock : function(){
27006         this.locked = false;
27007     },
27008
27009     /**
27010      * Returns true if the selections are locked.
27011      * @return {Boolean}
27012      */
27013     isLocked : function(){
27014         return this.locked;
27015     },
27016     
27017     
27018     initEvents : function ()
27019     {
27020         
27021     }
27022 });
27023 /**
27024  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27025  * @class Roo.bootstrap.Table.RowSelectionModel
27026  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27027  * It supports multiple selections and keyboard selection/navigation. 
27028  * @constructor
27029  * @param {Object} config
27030  */
27031
27032 Roo.bootstrap.Table.RowSelectionModel = function(config){
27033     Roo.apply(this, config);
27034     this.selections = new Roo.util.MixedCollection(false, function(o){
27035         return o.id;
27036     });
27037
27038     this.last = false;
27039     this.lastActive = false;
27040
27041     this.addEvents({
27042         /**
27043              * @event selectionchange
27044              * Fires when the selection changes
27045              * @param {SelectionModel} this
27046              */
27047             "selectionchange" : true,
27048         /**
27049              * @event afterselectionchange
27050              * Fires after the selection changes (eg. by key press or clicking)
27051              * @param {SelectionModel} this
27052              */
27053             "afterselectionchange" : true,
27054         /**
27055              * @event beforerowselect
27056              * Fires when a row is selected being selected, return false to cancel.
27057              * @param {SelectionModel} this
27058              * @param {Number} rowIndex The selected index
27059              * @param {Boolean} keepExisting False if other selections will be cleared
27060              */
27061             "beforerowselect" : true,
27062         /**
27063              * @event rowselect
27064              * Fires when a row is selected.
27065              * @param {SelectionModel} this
27066              * @param {Number} rowIndex The selected index
27067              * @param {Roo.data.Record} r The record
27068              */
27069             "rowselect" : true,
27070         /**
27071              * @event rowdeselect
27072              * Fires when a row is deselected.
27073              * @param {SelectionModel} this
27074              * @param {Number} rowIndex The selected index
27075              */
27076         "rowdeselect" : true
27077     });
27078     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27079     this.locked = false;
27080  };
27081
27082 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27083     /**
27084      * @cfg {Boolean} singleSelect
27085      * True to allow selection of only one row at a time (defaults to false)
27086      */
27087     singleSelect : false,
27088
27089     // private
27090     initEvents : function()
27091     {
27092
27093         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27094         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27095         //}else{ // allow click to work like normal
27096          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27097         //}
27098         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27099         this.grid.on("rowclick", this.handleMouseDown, this);
27100         
27101         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27102             "up" : function(e){
27103                 if(!e.shiftKey){
27104                     this.selectPrevious(e.shiftKey);
27105                 }else if(this.last !== false && this.lastActive !== false){
27106                     var last = this.last;
27107                     this.selectRange(this.last,  this.lastActive-1);
27108                     this.grid.getView().focusRow(this.lastActive);
27109                     if(last !== false){
27110                         this.last = last;
27111                     }
27112                 }else{
27113                     this.selectFirstRow();
27114                 }
27115                 this.fireEvent("afterselectionchange", this);
27116             },
27117             "down" : function(e){
27118                 if(!e.shiftKey){
27119                     this.selectNext(e.shiftKey);
27120                 }else if(this.last !== false && this.lastActive !== false){
27121                     var last = this.last;
27122                     this.selectRange(this.last,  this.lastActive+1);
27123                     this.grid.getView().focusRow(this.lastActive);
27124                     if(last !== false){
27125                         this.last = last;
27126                     }
27127                 }else{
27128                     this.selectFirstRow();
27129                 }
27130                 this.fireEvent("afterselectionchange", this);
27131             },
27132             scope: this
27133         });
27134         this.grid.store.on('load', function(){
27135             this.selections.clear();
27136         },this);
27137         /*
27138         var view = this.grid.view;
27139         view.on("refresh", this.onRefresh, this);
27140         view.on("rowupdated", this.onRowUpdated, this);
27141         view.on("rowremoved", this.onRemove, this);
27142         */
27143     },
27144
27145     // private
27146     onRefresh : function()
27147     {
27148         var ds = this.grid.store, i, v = this.grid.view;
27149         var s = this.selections;
27150         s.each(function(r){
27151             if((i = ds.indexOfId(r.id)) != -1){
27152                 v.onRowSelect(i);
27153             }else{
27154                 s.remove(r);
27155             }
27156         });
27157     },
27158
27159     // private
27160     onRemove : function(v, index, r){
27161         this.selections.remove(r);
27162     },
27163
27164     // private
27165     onRowUpdated : function(v, index, r){
27166         if(this.isSelected(r)){
27167             v.onRowSelect(index);
27168         }
27169     },
27170
27171     /**
27172      * Select records.
27173      * @param {Array} records The records to select
27174      * @param {Boolean} keepExisting (optional) True to keep existing selections
27175      */
27176     selectRecords : function(records, keepExisting)
27177     {
27178         if(!keepExisting){
27179             this.clearSelections();
27180         }
27181             var ds = this.grid.store;
27182         for(var i = 0, len = records.length; i < len; i++){
27183             this.selectRow(ds.indexOf(records[i]), true);
27184         }
27185     },
27186
27187     /**
27188      * Gets the number of selected rows.
27189      * @return {Number}
27190      */
27191     getCount : function(){
27192         return this.selections.length;
27193     },
27194
27195     /**
27196      * Selects the first row in the grid.
27197      */
27198     selectFirstRow : function(){
27199         this.selectRow(0);
27200     },
27201
27202     /**
27203      * Select the last row.
27204      * @param {Boolean} keepExisting (optional) True to keep existing selections
27205      */
27206     selectLastRow : function(keepExisting){
27207         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27208         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27209     },
27210
27211     /**
27212      * Selects the row immediately following the last selected row.
27213      * @param {Boolean} keepExisting (optional) True to keep existing selections
27214      */
27215     selectNext : function(keepExisting)
27216     {
27217             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27218             this.selectRow(this.last+1, keepExisting);
27219             this.grid.getView().focusRow(this.last);
27220         }
27221     },
27222
27223     /**
27224      * Selects the row that precedes the last selected row.
27225      * @param {Boolean} keepExisting (optional) True to keep existing selections
27226      */
27227     selectPrevious : function(keepExisting){
27228         if(this.last){
27229             this.selectRow(this.last-1, keepExisting);
27230             this.grid.getView().focusRow(this.last);
27231         }
27232     },
27233
27234     /**
27235      * Returns the selected records
27236      * @return {Array} Array of selected records
27237      */
27238     getSelections : function(){
27239         return [].concat(this.selections.items);
27240     },
27241
27242     /**
27243      * Returns the first selected record.
27244      * @return {Record}
27245      */
27246     getSelected : function(){
27247         return this.selections.itemAt(0);
27248     },
27249
27250
27251     /**
27252      * Clears all selections.
27253      */
27254     clearSelections : function(fast)
27255     {
27256         if(this.locked) {
27257             return;
27258         }
27259         if(fast !== true){
27260                 var ds = this.grid.store;
27261             var s = this.selections;
27262             s.each(function(r){
27263                 this.deselectRow(ds.indexOfId(r.id));
27264             }, this);
27265             s.clear();
27266         }else{
27267             this.selections.clear();
27268         }
27269         this.last = false;
27270     },
27271
27272
27273     /**
27274      * Selects all rows.
27275      */
27276     selectAll : function(){
27277         if(this.locked) {
27278             return;
27279         }
27280         this.selections.clear();
27281         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27282             this.selectRow(i, true);
27283         }
27284     },
27285
27286     /**
27287      * Returns True if there is a selection.
27288      * @return {Boolean}
27289      */
27290     hasSelection : function(){
27291         return this.selections.length > 0;
27292     },
27293
27294     /**
27295      * Returns True if the specified row is selected.
27296      * @param {Number/Record} record The record or index of the record to check
27297      * @return {Boolean}
27298      */
27299     isSelected : function(index){
27300             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27301         return (r && this.selections.key(r.id) ? true : false);
27302     },
27303
27304     /**
27305      * Returns True if the specified record id is selected.
27306      * @param {String} id The id of record to check
27307      * @return {Boolean}
27308      */
27309     isIdSelected : function(id){
27310         return (this.selections.key(id) ? true : false);
27311     },
27312
27313
27314     // private
27315     handleMouseDBClick : function(e, t){
27316         
27317     },
27318     // private
27319     handleMouseDown : function(e, t)
27320     {
27321             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27322         if(this.isLocked() || rowIndex < 0 ){
27323             return;
27324         };
27325         if(e.shiftKey && this.last !== false){
27326             var last = this.last;
27327             this.selectRange(last, rowIndex, e.ctrlKey);
27328             this.last = last; // reset the last
27329             t.focus();
27330     
27331         }else{
27332             var isSelected = this.isSelected(rowIndex);
27333             //Roo.log("select row:" + rowIndex);
27334             if(isSelected){
27335                 this.deselectRow(rowIndex);
27336             } else {
27337                         this.selectRow(rowIndex, true);
27338             }
27339     
27340             /*
27341                 if(e.button !== 0 && isSelected){
27342                 alert('rowIndex 2: ' + rowIndex);
27343                     view.focusRow(rowIndex);
27344                 }else if(e.ctrlKey && isSelected){
27345                     this.deselectRow(rowIndex);
27346                 }else if(!isSelected){
27347                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27348                     view.focusRow(rowIndex);
27349                 }
27350             */
27351         }
27352         this.fireEvent("afterselectionchange", this);
27353     },
27354     // private
27355     handleDragableRowClick :  function(grid, rowIndex, e) 
27356     {
27357         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27358             this.selectRow(rowIndex, false);
27359             grid.view.focusRow(rowIndex);
27360              this.fireEvent("afterselectionchange", this);
27361         }
27362     },
27363     
27364     /**
27365      * Selects multiple rows.
27366      * @param {Array} rows Array of the indexes of the row to select
27367      * @param {Boolean} keepExisting (optional) True to keep existing selections
27368      */
27369     selectRows : function(rows, keepExisting){
27370         if(!keepExisting){
27371             this.clearSelections();
27372         }
27373         for(var i = 0, len = rows.length; i < len; i++){
27374             this.selectRow(rows[i], true);
27375         }
27376     },
27377
27378     /**
27379      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27380      * @param {Number} startRow The index of the first row in the range
27381      * @param {Number} endRow The index of the last row in the range
27382      * @param {Boolean} keepExisting (optional) True to retain existing selections
27383      */
27384     selectRange : function(startRow, endRow, keepExisting){
27385         if(this.locked) {
27386             return;
27387         }
27388         if(!keepExisting){
27389             this.clearSelections();
27390         }
27391         if(startRow <= endRow){
27392             for(var i = startRow; i <= endRow; i++){
27393                 this.selectRow(i, true);
27394             }
27395         }else{
27396             for(var i = startRow; i >= endRow; i--){
27397                 this.selectRow(i, true);
27398             }
27399         }
27400     },
27401
27402     /**
27403      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27404      * @param {Number} startRow The index of the first row in the range
27405      * @param {Number} endRow The index of the last row in the range
27406      */
27407     deselectRange : function(startRow, endRow, preventViewNotify){
27408         if(this.locked) {
27409             return;
27410         }
27411         for(var i = startRow; i <= endRow; i++){
27412             this.deselectRow(i, preventViewNotify);
27413         }
27414     },
27415
27416     /**
27417      * Selects a row.
27418      * @param {Number} row The index of the row to select
27419      * @param {Boolean} keepExisting (optional) True to keep existing selections
27420      */
27421     selectRow : function(index, keepExisting, preventViewNotify)
27422     {
27423             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27424             return;
27425         }
27426         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27427             if(!keepExisting || this.singleSelect){
27428                 this.clearSelections();
27429             }
27430             
27431             var r = this.grid.store.getAt(index);
27432             //console.log('selectRow - record id :' + r.id);
27433             
27434             this.selections.add(r);
27435             this.last = this.lastActive = index;
27436             if(!preventViewNotify){
27437                 var proxy = new Roo.Element(
27438                                 this.grid.getRowDom(index)
27439                 );
27440                 proxy.addClass('bg-info info');
27441             }
27442             this.fireEvent("rowselect", this, index, r);
27443             this.fireEvent("selectionchange", this);
27444         }
27445     },
27446
27447     /**
27448      * Deselects a row.
27449      * @param {Number} row The index of the row to deselect
27450      */
27451     deselectRow : function(index, preventViewNotify)
27452     {
27453         if(this.locked) {
27454             return;
27455         }
27456         if(this.last == index){
27457             this.last = false;
27458         }
27459         if(this.lastActive == index){
27460             this.lastActive = false;
27461         }
27462         
27463         var r = this.grid.store.getAt(index);
27464         if (!r) {
27465             return;
27466         }
27467         
27468         this.selections.remove(r);
27469         //.console.log('deselectRow - record id :' + r.id);
27470         if(!preventViewNotify){
27471         
27472             var proxy = new Roo.Element(
27473                 this.grid.getRowDom(index)
27474             );
27475             proxy.removeClass('bg-info info');
27476         }
27477         this.fireEvent("rowdeselect", this, index);
27478         this.fireEvent("selectionchange", this);
27479     },
27480
27481     // private
27482     restoreLast : function(){
27483         if(this._last){
27484             this.last = this._last;
27485         }
27486     },
27487
27488     // private
27489     acceptsNav : function(row, col, cm){
27490         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27491     },
27492
27493     // private
27494     onEditorKey : function(field, e){
27495         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27496         if(k == e.TAB){
27497             e.stopEvent();
27498             ed.completeEdit();
27499             if(e.shiftKey){
27500                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27501             }else{
27502                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27503             }
27504         }else if(k == e.ENTER && !e.ctrlKey){
27505             e.stopEvent();
27506             ed.completeEdit();
27507             if(e.shiftKey){
27508                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27509             }else{
27510                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27511             }
27512         }else if(k == e.ESC){
27513             ed.cancelEdit();
27514         }
27515         if(newCell){
27516             g.startEditing(newCell[0], newCell[1]);
27517         }
27518     }
27519 });
27520 /*
27521  * Based on:
27522  * Ext JS Library 1.1.1
27523  * Copyright(c) 2006-2007, Ext JS, LLC.
27524  *
27525  * Originally Released Under LGPL - original licence link has changed is not relivant.
27526  *
27527  * Fork - LGPL
27528  * <script type="text/javascript">
27529  */
27530  
27531 /**
27532  * @class Roo.bootstrap.PagingToolbar
27533  * @extends Roo.bootstrap.NavSimplebar
27534  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27535  * @constructor
27536  * Create a new PagingToolbar
27537  * @param {Object} config The config object
27538  * @param {Roo.data.Store} store
27539  */
27540 Roo.bootstrap.PagingToolbar = function(config)
27541 {
27542     // old args format still supported... - xtype is prefered..
27543         // created from xtype...
27544     
27545     this.ds = config.dataSource;
27546     
27547     if (config.store && !this.ds) {
27548         this.store= Roo.factory(config.store, Roo.data);
27549         this.ds = this.store;
27550         this.ds.xmodule = this.xmodule || false;
27551     }
27552     
27553     this.toolbarItems = [];
27554     if (config.items) {
27555         this.toolbarItems = config.items;
27556     }
27557     
27558     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27559     
27560     this.cursor = 0;
27561     
27562     if (this.ds) { 
27563         this.bind(this.ds);
27564     }
27565     
27566     if (Roo.bootstrap.version == 4) {
27567         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27568     } else {
27569         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27570     }
27571     
27572 };
27573
27574 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27575     /**
27576      * @cfg {Roo.data.Store} dataSource
27577      * The underlying data store providing the paged data
27578      */
27579     /**
27580      * @cfg {String/HTMLElement/Element} container
27581      * container The id or element that will contain the toolbar
27582      */
27583     /**
27584      * @cfg {Boolean} displayInfo
27585      * True to display the displayMsg (defaults to false)
27586      */
27587     /**
27588      * @cfg {Number} pageSize
27589      * The number of records to display per page (defaults to 20)
27590      */
27591     pageSize: 20,
27592     /**
27593      * @cfg {String} displayMsg
27594      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27595      */
27596     displayMsg : 'Displaying {0} - {1} of {2}',
27597     /**
27598      * @cfg {String} emptyMsg
27599      * The message to display when no records are found (defaults to "No data to display")
27600      */
27601     emptyMsg : 'No data to display',
27602     /**
27603      * Customizable piece of the default paging text (defaults to "Page")
27604      * @type String
27605      */
27606     beforePageText : "Page",
27607     /**
27608      * Customizable piece of the default paging text (defaults to "of %0")
27609      * @type String
27610      */
27611     afterPageText : "of {0}",
27612     /**
27613      * Customizable piece of the default paging text (defaults to "First Page")
27614      * @type String
27615      */
27616     firstText : "First Page",
27617     /**
27618      * Customizable piece of the default paging text (defaults to "Previous Page")
27619      * @type String
27620      */
27621     prevText : "Previous Page",
27622     /**
27623      * Customizable piece of the default paging text (defaults to "Next Page")
27624      * @type String
27625      */
27626     nextText : "Next Page",
27627     /**
27628      * Customizable piece of the default paging text (defaults to "Last Page")
27629      * @type String
27630      */
27631     lastText : "Last Page",
27632     /**
27633      * Customizable piece of the default paging text (defaults to "Refresh")
27634      * @type String
27635      */
27636     refreshText : "Refresh",
27637
27638     buttons : false,
27639     // private
27640     onRender : function(ct, position) 
27641     {
27642         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27643         this.navgroup.parentId = this.id;
27644         this.navgroup.onRender(this.el, null);
27645         // add the buttons to the navgroup
27646         
27647         if(this.displayInfo){
27648             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27649             this.displayEl = this.el.select('.x-paging-info', true).first();
27650 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27651 //            this.displayEl = navel.el.select('span',true).first();
27652         }
27653         
27654         var _this = this;
27655         
27656         if(this.buttons){
27657             Roo.each(_this.buttons, function(e){ // this might need to use render????
27658                Roo.factory(e).render(_this.el);
27659             });
27660         }
27661             
27662         Roo.each(_this.toolbarItems, function(e) {
27663             _this.navgroup.addItem(e);
27664         });
27665         
27666         
27667         this.first = this.navgroup.addItem({
27668             tooltip: this.firstText,
27669             cls: "prev btn-outline-secondary",
27670             html : ' <i class="fa fa-step-backward"></i>',
27671             disabled: true,
27672             preventDefault: true,
27673             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27674         });
27675         
27676         this.prev =  this.navgroup.addItem({
27677             tooltip: this.prevText,
27678             cls: "prev btn-outline-secondary",
27679             html : ' <i class="fa fa-backward"></i>',
27680             disabled: true,
27681             preventDefault: true,
27682             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27683         });
27684     //this.addSeparator();
27685         
27686         
27687         var field = this.navgroup.addItem( {
27688             tagtype : 'span',
27689             cls : 'x-paging-position  btn-outline-secondary',
27690              disabled: true,
27691             html : this.beforePageText  +
27692                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27693                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27694          } ); //?? escaped?
27695         
27696         this.field = field.el.select('input', true).first();
27697         this.field.on("keydown", this.onPagingKeydown, this);
27698         this.field.on("focus", function(){this.dom.select();});
27699     
27700     
27701         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27702         //this.field.setHeight(18);
27703         //this.addSeparator();
27704         this.next = this.navgroup.addItem({
27705             tooltip: this.nextText,
27706             cls: "next btn-outline-secondary",
27707             html : ' <i class="fa fa-forward"></i>',
27708             disabled: true,
27709             preventDefault: true,
27710             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27711         });
27712         this.last = this.navgroup.addItem({
27713             tooltip: this.lastText,
27714             html : ' <i class="fa fa-step-forward"></i>',
27715             cls: "next btn-outline-secondary",
27716             disabled: true,
27717             preventDefault: true,
27718             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27719         });
27720     //this.addSeparator();
27721         this.loading = this.navgroup.addItem({
27722             tooltip: this.refreshText,
27723             cls: "btn-outline-secondary",
27724             html : ' <i class="fa fa-refresh"></i>',
27725             preventDefault: true,
27726             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27727         });
27728         
27729     },
27730
27731     // private
27732     updateInfo : function(){
27733         if(this.displayEl){
27734             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27735             var msg = count == 0 ?
27736                 this.emptyMsg :
27737                 String.format(
27738                     this.displayMsg,
27739                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27740                 );
27741             this.displayEl.update(msg);
27742         }
27743     },
27744
27745     // private
27746     onLoad : function(ds, r, o)
27747     {
27748         this.cursor = o.params && o.params.start ? o.params.start : 0;
27749         
27750         var d = this.getPageData(),
27751             ap = d.activePage,
27752             ps = d.pages;
27753         
27754         
27755         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27756         this.field.dom.value = ap;
27757         this.first.setDisabled(ap == 1);
27758         this.prev.setDisabled(ap == 1);
27759         this.next.setDisabled(ap == ps);
27760         this.last.setDisabled(ap == ps);
27761         this.loading.enable();
27762         this.updateInfo();
27763     },
27764
27765     // private
27766     getPageData : function(){
27767         var total = this.ds.getTotalCount();
27768         return {
27769             total : total,
27770             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27771             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27772         };
27773     },
27774
27775     // private
27776     onLoadError : function(){
27777         this.loading.enable();
27778     },
27779
27780     // private
27781     onPagingKeydown : function(e){
27782         var k = e.getKey();
27783         var d = this.getPageData();
27784         if(k == e.RETURN){
27785             var v = this.field.dom.value, pageNum;
27786             if(!v || isNaN(pageNum = parseInt(v, 10))){
27787                 this.field.dom.value = d.activePage;
27788                 return;
27789             }
27790             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27791             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27792             e.stopEvent();
27793         }
27794         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))
27795         {
27796           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27797           this.field.dom.value = pageNum;
27798           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27799           e.stopEvent();
27800         }
27801         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27802         {
27803           var v = this.field.dom.value, pageNum; 
27804           var increment = (e.shiftKey) ? 10 : 1;
27805           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27806                 increment *= -1;
27807           }
27808           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27809             this.field.dom.value = d.activePage;
27810             return;
27811           }
27812           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27813           {
27814             this.field.dom.value = parseInt(v, 10) + increment;
27815             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27816             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27817           }
27818           e.stopEvent();
27819         }
27820     },
27821
27822     // private
27823     beforeLoad : function(){
27824         if(this.loading){
27825             this.loading.disable();
27826         }
27827     },
27828
27829     // private
27830     onClick : function(which){
27831         
27832         var ds = this.ds;
27833         if (!ds) {
27834             return;
27835         }
27836         
27837         switch(which){
27838             case "first":
27839                 ds.load({params:{start: 0, limit: this.pageSize}});
27840             break;
27841             case "prev":
27842                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27843             break;
27844             case "next":
27845                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27846             break;
27847             case "last":
27848                 var total = ds.getTotalCount();
27849                 var extra = total % this.pageSize;
27850                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27851                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27852             break;
27853             case "refresh":
27854                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27855             break;
27856         }
27857     },
27858
27859     /**
27860      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27861      * @param {Roo.data.Store} store The data store to unbind
27862      */
27863     unbind : function(ds){
27864         ds.un("beforeload", this.beforeLoad, this);
27865         ds.un("load", this.onLoad, this);
27866         ds.un("loadexception", this.onLoadError, this);
27867         ds.un("remove", this.updateInfo, this);
27868         ds.un("add", this.updateInfo, this);
27869         this.ds = undefined;
27870     },
27871
27872     /**
27873      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27874      * @param {Roo.data.Store} store The data store to bind
27875      */
27876     bind : function(ds){
27877         ds.on("beforeload", this.beforeLoad, this);
27878         ds.on("load", this.onLoad, this);
27879         ds.on("loadexception", this.onLoadError, this);
27880         ds.on("remove", this.updateInfo, this);
27881         ds.on("add", this.updateInfo, this);
27882         this.ds = ds;
27883     }
27884 });/*
27885  * - LGPL
27886  *
27887  * element
27888  * 
27889  */
27890
27891 /**
27892  * @class Roo.bootstrap.MessageBar
27893  * @extends Roo.bootstrap.Component
27894  * Bootstrap MessageBar class
27895  * @cfg {String} html contents of the MessageBar
27896  * @cfg {String} weight (info | success | warning | danger) default info
27897  * @cfg {String} beforeClass insert the bar before the given class
27898  * @cfg {Boolean} closable (true | false) default false
27899  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27900  * 
27901  * @constructor
27902  * Create a new Element
27903  * @param {Object} config The config object
27904  */
27905
27906 Roo.bootstrap.MessageBar = function(config){
27907     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27908 };
27909
27910 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27911     
27912     html: '',
27913     weight: 'info',
27914     closable: false,
27915     fixed: false,
27916     beforeClass: 'bootstrap-sticky-wrap',
27917     
27918     getAutoCreate : function(){
27919         
27920         var cfg = {
27921             tag: 'div',
27922             cls: 'alert alert-dismissable alert-' + this.weight,
27923             cn: [
27924                 {
27925                     tag: 'span',
27926                     cls: 'message',
27927                     html: this.html || ''
27928                 }
27929             ]
27930         };
27931         
27932         if(this.fixed){
27933             cfg.cls += ' alert-messages-fixed';
27934         }
27935         
27936         if(this.closable){
27937             cfg.cn.push({
27938                 tag: 'button',
27939                 cls: 'close',
27940                 html: 'x'
27941             });
27942         }
27943         
27944         return cfg;
27945     },
27946     
27947     onRender : function(ct, position)
27948     {
27949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27950         
27951         if(!this.el){
27952             var cfg = Roo.apply({},  this.getAutoCreate());
27953             cfg.id = Roo.id();
27954             
27955             if (this.cls) {
27956                 cfg.cls += ' ' + this.cls;
27957             }
27958             if (this.style) {
27959                 cfg.style = this.style;
27960             }
27961             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27962             
27963             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27964         }
27965         
27966         this.el.select('>button.close').on('click', this.hide, this);
27967         
27968     },
27969     
27970     show : function()
27971     {
27972         if (!this.rendered) {
27973             this.render();
27974         }
27975         
27976         this.el.show();
27977         
27978         this.fireEvent('show', this);
27979         
27980     },
27981     
27982     hide : function()
27983     {
27984         if (!this.rendered) {
27985             this.render();
27986         }
27987         
27988         this.el.hide();
27989         
27990         this.fireEvent('hide', this);
27991     },
27992     
27993     update : function()
27994     {
27995 //        var e = this.el.dom.firstChild;
27996 //        
27997 //        if(this.closable){
27998 //            e = e.nextSibling;
27999 //        }
28000 //        
28001 //        e.data = this.html || '';
28002
28003         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28004     }
28005    
28006 });
28007
28008  
28009
28010      /*
28011  * - LGPL
28012  *
28013  * Graph
28014  * 
28015  */
28016
28017
28018 /**
28019  * @class Roo.bootstrap.Graph
28020  * @extends Roo.bootstrap.Component
28021  * Bootstrap Graph class
28022 > Prameters
28023  -sm {number} sm 4
28024  -md {number} md 5
28025  @cfg {String} graphtype  bar | vbar | pie
28026  @cfg {number} g_x coodinator | centre x (pie)
28027  @cfg {number} g_y coodinator | centre y (pie)
28028  @cfg {number} g_r radius (pie)
28029  @cfg {number} g_height height of the chart (respected by all elements in the set)
28030  @cfg {number} g_width width of the chart (respected by all elements in the set)
28031  @cfg {Object} title The title of the chart
28032     
28033  -{Array}  values
28034  -opts (object) options for the chart 
28035      o {
28036      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28037      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28038      o vgutter (number)
28039      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.
28040      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28041      o to
28042      o stretch (boolean)
28043      o }
28044  -opts (object) options for the pie
28045      o{
28046      o cut
28047      o startAngle (number)
28048      o endAngle (number)
28049      } 
28050  *
28051  * @constructor
28052  * Create a new Input
28053  * @param {Object} config The config object
28054  */
28055
28056 Roo.bootstrap.Graph = function(config){
28057     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28058     
28059     this.addEvents({
28060         // img events
28061         /**
28062          * @event click
28063          * The img click event for the img.
28064          * @param {Roo.EventObject} e
28065          */
28066         "click" : true
28067     });
28068 };
28069
28070 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28071     
28072     sm: 4,
28073     md: 5,
28074     graphtype: 'bar',
28075     g_height: 250,
28076     g_width: 400,
28077     g_x: 50,
28078     g_y: 50,
28079     g_r: 30,
28080     opts:{
28081         //g_colors: this.colors,
28082         g_type: 'soft',
28083         g_gutter: '20%'
28084
28085     },
28086     title : false,
28087
28088     getAutoCreate : function(){
28089         
28090         var cfg = {
28091             tag: 'div',
28092             html : null
28093         };
28094         
28095         
28096         return  cfg;
28097     },
28098
28099     onRender : function(ct,position){
28100         
28101         
28102         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28103         
28104         if (typeof(Raphael) == 'undefined') {
28105             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28106             return;
28107         }
28108         
28109         this.raphael = Raphael(this.el.dom);
28110         
28111                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28112                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28113                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28114                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28115                 /*
28116                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28117                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28118                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28119                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28120                 
28121                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28122                 r.barchart(330, 10, 300, 220, data1);
28123                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28124                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28125                 */
28126                 
28127                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28128                 // r.barchart(30, 30, 560, 250,  xdata, {
28129                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28130                 //     axis : "0 0 1 1",
28131                 //     axisxlabels :  xdata
28132                 //     //yvalues : cols,
28133                    
28134                 // });
28135 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28136 //        
28137 //        this.load(null,xdata,{
28138 //                axis : "0 0 1 1",
28139 //                axisxlabels :  xdata
28140 //                });
28141
28142     },
28143
28144     load : function(graphtype,xdata,opts)
28145     {
28146         this.raphael.clear();
28147         if(!graphtype) {
28148             graphtype = this.graphtype;
28149         }
28150         if(!opts){
28151             opts = this.opts;
28152         }
28153         var r = this.raphael,
28154             fin = function () {
28155                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28156             },
28157             fout = function () {
28158                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28159             },
28160             pfin = function() {
28161                 this.sector.stop();
28162                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28163
28164                 if (this.label) {
28165                     this.label[0].stop();
28166                     this.label[0].attr({ r: 7.5 });
28167                     this.label[1].attr({ "font-weight": 800 });
28168                 }
28169             },
28170             pfout = function() {
28171                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28172
28173                 if (this.label) {
28174                     this.label[0].animate({ r: 5 }, 500, "bounce");
28175                     this.label[1].attr({ "font-weight": 400 });
28176                 }
28177             };
28178
28179         switch(graphtype){
28180             case 'bar':
28181                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28182                 break;
28183             case 'hbar':
28184                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28185                 break;
28186             case 'pie':
28187 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28188 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28189 //            
28190                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28191                 
28192                 break;
28193
28194         }
28195         
28196         if(this.title){
28197             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28198         }
28199         
28200     },
28201     
28202     setTitle: function(o)
28203     {
28204         this.title = o;
28205     },
28206     
28207     initEvents: function() {
28208         
28209         if(!this.href){
28210             this.el.on('click', this.onClick, this);
28211         }
28212     },
28213     
28214     onClick : function(e)
28215     {
28216         Roo.log('img onclick');
28217         this.fireEvent('click', this, e);
28218     }
28219    
28220 });
28221
28222  
28223 /*
28224  * - LGPL
28225  *
28226  * numberBox
28227  * 
28228  */
28229 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28230
28231 /**
28232  * @class Roo.bootstrap.dash.NumberBox
28233  * @extends Roo.bootstrap.Component
28234  * Bootstrap NumberBox class
28235  * @cfg {String} headline Box headline
28236  * @cfg {String} content Box content
28237  * @cfg {String} icon Box icon
28238  * @cfg {String} footer Footer text
28239  * @cfg {String} fhref Footer href
28240  * 
28241  * @constructor
28242  * Create a new NumberBox
28243  * @param {Object} config The config object
28244  */
28245
28246
28247 Roo.bootstrap.dash.NumberBox = function(config){
28248     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28249     
28250 };
28251
28252 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28253     
28254     headline : '',
28255     content : '',
28256     icon : '',
28257     footer : '',
28258     fhref : '',
28259     ficon : '',
28260     
28261     getAutoCreate : function(){
28262         
28263         var cfg = {
28264             tag : 'div',
28265             cls : 'small-box ',
28266             cn : [
28267                 {
28268                     tag : 'div',
28269                     cls : 'inner',
28270                     cn :[
28271                         {
28272                             tag : 'h3',
28273                             cls : 'roo-headline',
28274                             html : this.headline
28275                         },
28276                         {
28277                             tag : 'p',
28278                             cls : 'roo-content',
28279                             html : this.content
28280                         }
28281                     ]
28282                 }
28283             ]
28284         };
28285         
28286         if(this.icon){
28287             cfg.cn.push({
28288                 tag : 'div',
28289                 cls : 'icon',
28290                 cn :[
28291                     {
28292                         tag : 'i',
28293                         cls : 'ion ' + this.icon
28294                     }
28295                 ]
28296             });
28297         }
28298         
28299         if(this.footer){
28300             var footer = {
28301                 tag : 'a',
28302                 cls : 'small-box-footer',
28303                 href : this.fhref || '#',
28304                 html : this.footer
28305             };
28306             
28307             cfg.cn.push(footer);
28308             
28309         }
28310         
28311         return  cfg;
28312     },
28313
28314     onRender : function(ct,position){
28315         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28316
28317
28318        
28319                 
28320     },
28321
28322     setHeadline: function (value)
28323     {
28324         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28325     },
28326     
28327     setFooter: function (value, href)
28328     {
28329         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28330         
28331         if(href){
28332             this.el.select('a.small-box-footer',true).first().attr('href', href);
28333         }
28334         
28335     },
28336
28337     setContent: function (value)
28338     {
28339         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28340     },
28341
28342     initEvents: function() 
28343     {   
28344         
28345     }
28346     
28347 });
28348
28349  
28350 /*
28351  * - LGPL
28352  *
28353  * TabBox
28354  * 
28355  */
28356 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28357
28358 /**
28359  * @class Roo.bootstrap.dash.TabBox
28360  * @extends Roo.bootstrap.Component
28361  * Bootstrap TabBox class
28362  * @cfg {String} title Title of the TabBox
28363  * @cfg {String} icon Icon of the TabBox
28364  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28365  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28366  * 
28367  * @constructor
28368  * Create a new TabBox
28369  * @param {Object} config The config object
28370  */
28371
28372
28373 Roo.bootstrap.dash.TabBox = function(config){
28374     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28375     this.addEvents({
28376         // raw events
28377         /**
28378          * @event addpane
28379          * When a pane is added
28380          * @param {Roo.bootstrap.dash.TabPane} pane
28381          */
28382         "addpane" : true,
28383         /**
28384          * @event activatepane
28385          * When a pane is activated
28386          * @param {Roo.bootstrap.dash.TabPane} pane
28387          */
28388         "activatepane" : true
28389         
28390          
28391     });
28392     
28393     this.panes = [];
28394 };
28395
28396 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28397
28398     title : '',
28399     icon : false,
28400     showtabs : true,
28401     tabScrollable : false,
28402     
28403     getChildContainer : function()
28404     {
28405         return this.el.select('.tab-content', true).first();
28406     },
28407     
28408     getAutoCreate : function(){
28409         
28410         var header = {
28411             tag: 'li',
28412             cls: 'pull-left header',
28413             html: this.title,
28414             cn : []
28415         };
28416         
28417         if(this.icon){
28418             header.cn.push({
28419                 tag: 'i',
28420                 cls: 'fa ' + this.icon
28421             });
28422         }
28423         
28424         var h = {
28425             tag: 'ul',
28426             cls: 'nav nav-tabs pull-right',
28427             cn: [
28428                 header
28429             ]
28430         };
28431         
28432         if(this.tabScrollable){
28433             h = {
28434                 tag: 'div',
28435                 cls: 'tab-header',
28436                 cn: [
28437                     {
28438                         tag: 'ul',
28439                         cls: 'nav nav-tabs pull-right',
28440                         cn: [
28441                             header
28442                         ]
28443                     }
28444                 ]
28445             };
28446         }
28447         
28448         var cfg = {
28449             tag: 'div',
28450             cls: 'nav-tabs-custom',
28451             cn: [
28452                 h,
28453                 {
28454                     tag: 'div',
28455                     cls: 'tab-content no-padding',
28456                     cn: []
28457                 }
28458             ]
28459         };
28460
28461         return  cfg;
28462     },
28463     initEvents : function()
28464     {
28465         //Roo.log('add add pane handler');
28466         this.on('addpane', this.onAddPane, this);
28467     },
28468      /**
28469      * Updates the box title
28470      * @param {String} html to set the title to.
28471      */
28472     setTitle : function(value)
28473     {
28474         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28475     },
28476     onAddPane : function(pane)
28477     {
28478         this.panes.push(pane);
28479         //Roo.log('addpane');
28480         //Roo.log(pane);
28481         // tabs are rendere left to right..
28482         if(!this.showtabs){
28483             return;
28484         }
28485         
28486         var ctr = this.el.select('.nav-tabs', true).first();
28487          
28488          
28489         var existing = ctr.select('.nav-tab',true);
28490         var qty = existing.getCount();;
28491         
28492         
28493         var tab = ctr.createChild({
28494             tag : 'li',
28495             cls : 'nav-tab' + (qty ? '' : ' active'),
28496             cn : [
28497                 {
28498                     tag : 'a',
28499                     href:'#',
28500                     html : pane.title
28501                 }
28502             ]
28503         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28504         pane.tab = tab;
28505         
28506         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28507         if (!qty) {
28508             pane.el.addClass('active');
28509         }
28510         
28511                 
28512     },
28513     onTabClick : function(ev,un,ob,pane)
28514     {
28515         //Roo.log('tab - prev default');
28516         ev.preventDefault();
28517         
28518         
28519         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28520         pane.tab.addClass('active');
28521         //Roo.log(pane.title);
28522         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28523         // technically we should have a deactivate event.. but maybe add later.
28524         // and it should not de-activate the selected tab...
28525         this.fireEvent('activatepane', pane);
28526         pane.el.addClass('active');
28527         pane.fireEvent('activate');
28528         
28529         
28530     },
28531     
28532     getActivePane : function()
28533     {
28534         var r = false;
28535         Roo.each(this.panes, function(p) {
28536             if(p.el.hasClass('active')){
28537                 r = p;
28538                 return false;
28539             }
28540             
28541             return;
28542         });
28543         
28544         return r;
28545     }
28546     
28547     
28548 });
28549
28550  
28551 /*
28552  * - LGPL
28553  *
28554  * Tab pane
28555  * 
28556  */
28557 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28558 /**
28559  * @class Roo.bootstrap.TabPane
28560  * @extends Roo.bootstrap.Component
28561  * Bootstrap TabPane class
28562  * @cfg {Boolean} active (false | true) Default false
28563  * @cfg {String} title title of panel
28564
28565  * 
28566  * @constructor
28567  * Create a new TabPane
28568  * @param {Object} config The config object
28569  */
28570
28571 Roo.bootstrap.dash.TabPane = function(config){
28572     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28573     
28574     this.addEvents({
28575         // raw events
28576         /**
28577          * @event activate
28578          * When a pane is activated
28579          * @param {Roo.bootstrap.dash.TabPane} pane
28580          */
28581         "activate" : true
28582          
28583     });
28584 };
28585
28586 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28587     
28588     active : false,
28589     title : '',
28590     
28591     // the tabBox that this is attached to.
28592     tab : false,
28593      
28594     getAutoCreate : function() 
28595     {
28596         var cfg = {
28597             tag: 'div',
28598             cls: 'tab-pane'
28599         };
28600         
28601         if(this.active){
28602             cfg.cls += ' active';
28603         }
28604         
28605         return cfg;
28606     },
28607     initEvents  : function()
28608     {
28609         //Roo.log('trigger add pane handler');
28610         this.parent().fireEvent('addpane', this)
28611     },
28612     
28613      /**
28614      * Updates the tab title 
28615      * @param {String} html to set the title to.
28616      */
28617     setTitle: function(str)
28618     {
28619         if (!this.tab) {
28620             return;
28621         }
28622         this.title = str;
28623         this.tab.select('a', true).first().dom.innerHTML = str;
28624         
28625     }
28626     
28627     
28628     
28629 });
28630
28631  
28632
28633
28634  /*
28635  * - LGPL
28636  *
28637  * menu
28638  * 
28639  */
28640 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28641
28642 /**
28643  * @class Roo.bootstrap.menu.Menu
28644  * @extends Roo.bootstrap.Component
28645  * Bootstrap Menu class - container for Menu
28646  * @cfg {String} html Text of the menu
28647  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28648  * @cfg {String} icon Font awesome icon
28649  * @cfg {String} pos Menu align to (top | bottom) default bottom
28650  * 
28651  * 
28652  * @constructor
28653  * Create a new Menu
28654  * @param {Object} config The config object
28655  */
28656
28657
28658 Roo.bootstrap.menu.Menu = function(config){
28659     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28660     
28661     this.addEvents({
28662         /**
28663          * @event beforeshow
28664          * Fires before this menu is displayed
28665          * @param {Roo.bootstrap.menu.Menu} this
28666          */
28667         beforeshow : true,
28668         /**
28669          * @event beforehide
28670          * Fires before this menu is hidden
28671          * @param {Roo.bootstrap.menu.Menu} this
28672          */
28673         beforehide : true,
28674         /**
28675          * @event show
28676          * Fires after this menu is displayed
28677          * @param {Roo.bootstrap.menu.Menu} this
28678          */
28679         show : true,
28680         /**
28681          * @event hide
28682          * Fires after this menu is hidden
28683          * @param {Roo.bootstrap.menu.Menu} this
28684          */
28685         hide : true,
28686         /**
28687          * @event click
28688          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28689          * @param {Roo.bootstrap.menu.Menu} this
28690          * @param {Roo.EventObject} e
28691          */
28692         click : true
28693     });
28694     
28695 };
28696
28697 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28698     
28699     submenu : false,
28700     html : '',
28701     weight : 'default',
28702     icon : false,
28703     pos : 'bottom',
28704     
28705     
28706     getChildContainer : function() {
28707         if(this.isSubMenu){
28708             return this.el;
28709         }
28710         
28711         return this.el.select('ul.dropdown-menu', true).first();  
28712     },
28713     
28714     getAutoCreate : function()
28715     {
28716         var text = [
28717             {
28718                 tag : 'span',
28719                 cls : 'roo-menu-text',
28720                 html : this.html
28721             }
28722         ];
28723         
28724         if(this.icon){
28725             text.unshift({
28726                 tag : 'i',
28727                 cls : 'fa ' + this.icon
28728             })
28729         }
28730         
28731         
28732         var cfg = {
28733             tag : 'div',
28734             cls : 'btn-group',
28735             cn : [
28736                 {
28737                     tag : 'button',
28738                     cls : 'dropdown-button btn btn-' + this.weight,
28739                     cn : text
28740                 },
28741                 {
28742                     tag : 'button',
28743                     cls : 'dropdown-toggle btn btn-' + this.weight,
28744                     cn : [
28745                         {
28746                             tag : 'span',
28747                             cls : 'caret'
28748                         }
28749                     ]
28750                 },
28751                 {
28752                     tag : 'ul',
28753                     cls : 'dropdown-menu'
28754                 }
28755             ]
28756             
28757         };
28758         
28759         if(this.pos == 'top'){
28760             cfg.cls += ' dropup';
28761         }
28762         
28763         if(this.isSubMenu){
28764             cfg = {
28765                 tag : 'ul',
28766                 cls : 'dropdown-menu'
28767             }
28768         }
28769         
28770         return cfg;
28771     },
28772     
28773     onRender : function(ct, position)
28774     {
28775         this.isSubMenu = ct.hasClass('dropdown-submenu');
28776         
28777         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28778     },
28779     
28780     initEvents : function() 
28781     {
28782         if(this.isSubMenu){
28783             return;
28784         }
28785         
28786         this.hidden = true;
28787         
28788         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28789         this.triggerEl.on('click', this.onTriggerPress, this);
28790         
28791         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28792         this.buttonEl.on('click', this.onClick, this);
28793         
28794     },
28795     
28796     list : function()
28797     {
28798         if(this.isSubMenu){
28799             return this.el;
28800         }
28801         
28802         return this.el.select('ul.dropdown-menu', true).first();
28803     },
28804     
28805     onClick : function(e)
28806     {
28807         this.fireEvent("click", this, e);
28808     },
28809     
28810     onTriggerPress  : function(e)
28811     {   
28812         if (this.isVisible()) {
28813             this.hide();
28814         } else {
28815             this.show();
28816         }
28817     },
28818     
28819     isVisible : function(){
28820         return !this.hidden;
28821     },
28822     
28823     show : function()
28824     {
28825         this.fireEvent("beforeshow", this);
28826         
28827         this.hidden = false;
28828         this.el.addClass('open');
28829         
28830         Roo.get(document).on("mouseup", this.onMouseUp, this);
28831         
28832         this.fireEvent("show", this);
28833         
28834         
28835     },
28836     
28837     hide : function()
28838     {
28839         this.fireEvent("beforehide", this);
28840         
28841         this.hidden = true;
28842         this.el.removeClass('open');
28843         
28844         Roo.get(document).un("mouseup", this.onMouseUp);
28845         
28846         this.fireEvent("hide", this);
28847     },
28848     
28849     onMouseUp : function()
28850     {
28851         this.hide();
28852     }
28853     
28854 });
28855
28856  
28857  /*
28858  * - LGPL
28859  *
28860  * menu item
28861  * 
28862  */
28863 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28864
28865 /**
28866  * @class Roo.bootstrap.menu.Item
28867  * @extends Roo.bootstrap.Component
28868  * Bootstrap MenuItem class
28869  * @cfg {Boolean} submenu (true | false) default false
28870  * @cfg {String} html text of the item
28871  * @cfg {String} href the link
28872  * @cfg {Boolean} disable (true | false) default false
28873  * @cfg {Boolean} preventDefault (true | false) default true
28874  * @cfg {String} icon Font awesome icon
28875  * @cfg {String} pos Submenu align to (left | right) default right 
28876  * 
28877  * 
28878  * @constructor
28879  * Create a new Item
28880  * @param {Object} config The config object
28881  */
28882
28883
28884 Roo.bootstrap.menu.Item = function(config){
28885     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28886     this.addEvents({
28887         /**
28888          * @event mouseover
28889          * Fires when the mouse is hovering over this menu
28890          * @param {Roo.bootstrap.menu.Item} this
28891          * @param {Roo.EventObject} e
28892          */
28893         mouseover : true,
28894         /**
28895          * @event mouseout
28896          * Fires when the mouse exits this menu
28897          * @param {Roo.bootstrap.menu.Item} this
28898          * @param {Roo.EventObject} e
28899          */
28900         mouseout : true,
28901         // raw events
28902         /**
28903          * @event click
28904          * The raw click event for the entire grid.
28905          * @param {Roo.EventObject} e
28906          */
28907         click : true
28908     });
28909 };
28910
28911 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28912     
28913     submenu : false,
28914     href : '',
28915     html : '',
28916     preventDefault: true,
28917     disable : false,
28918     icon : false,
28919     pos : 'right',
28920     
28921     getAutoCreate : function()
28922     {
28923         var text = [
28924             {
28925                 tag : 'span',
28926                 cls : 'roo-menu-item-text',
28927                 html : this.html
28928             }
28929         ];
28930         
28931         if(this.icon){
28932             text.unshift({
28933                 tag : 'i',
28934                 cls : 'fa ' + this.icon
28935             })
28936         }
28937         
28938         var cfg = {
28939             tag : 'li',
28940             cn : [
28941                 {
28942                     tag : 'a',
28943                     href : this.href || '#',
28944                     cn : text
28945                 }
28946             ]
28947         };
28948         
28949         if(this.disable){
28950             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28951         }
28952         
28953         if(this.submenu){
28954             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28955             
28956             if(this.pos == 'left'){
28957                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28958             }
28959         }
28960         
28961         return cfg;
28962     },
28963     
28964     initEvents : function() 
28965     {
28966         this.el.on('mouseover', this.onMouseOver, this);
28967         this.el.on('mouseout', this.onMouseOut, this);
28968         
28969         this.el.select('a', true).first().on('click', this.onClick, this);
28970         
28971     },
28972     
28973     onClick : function(e)
28974     {
28975         if(this.preventDefault){
28976             e.preventDefault();
28977         }
28978         
28979         this.fireEvent("click", this, e);
28980     },
28981     
28982     onMouseOver : function(e)
28983     {
28984         if(this.submenu && this.pos == 'left'){
28985             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28986         }
28987         
28988         this.fireEvent("mouseover", this, e);
28989     },
28990     
28991     onMouseOut : function(e)
28992     {
28993         this.fireEvent("mouseout", this, e);
28994     }
28995 });
28996
28997  
28998
28999  /*
29000  * - LGPL
29001  *
29002  * menu separator
29003  * 
29004  */
29005 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29006
29007 /**
29008  * @class Roo.bootstrap.menu.Separator
29009  * @extends Roo.bootstrap.Component
29010  * Bootstrap Separator class
29011  * 
29012  * @constructor
29013  * Create a new Separator
29014  * @param {Object} config The config object
29015  */
29016
29017
29018 Roo.bootstrap.menu.Separator = function(config){
29019     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29020 };
29021
29022 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29023     
29024     getAutoCreate : function(){
29025         var cfg = {
29026             tag : 'li',
29027             cls: 'dropdown-divider divider'
29028         };
29029         
29030         return cfg;
29031     }
29032    
29033 });
29034
29035  
29036
29037  /*
29038  * - LGPL
29039  *
29040  * Tooltip
29041  * 
29042  */
29043
29044 /**
29045  * @class Roo.bootstrap.Tooltip
29046  * Bootstrap Tooltip class
29047  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29048  * to determine which dom element triggers the tooltip.
29049  * 
29050  * It needs to add support for additional attributes like tooltip-position
29051  * 
29052  * @constructor
29053  * Create a new Toolti
29054  * @param {Object} config The config object
29055  */
29056
29057 Roo.bootstrap.Tooltip = function(config){
29058     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29059     
29060     this.alignment = Roo.bootstrap.Tooltip.alignment;
29061     
29062     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29063         this.alignment = config.alignment;
29064     }
29065     
29066 };
29067
29068 Roo.apply(Roo.bootstrap.Tooltip, {
29069     /**
29070      * @function init initialize tooltip monitoring.
29071      * @static
29072      */
29073     currentEl : false,
29074     currentTip : false,
29075     currentRegion : false,
29076     
29077     //  init : delay?
29078     
29079     init : function()
29080     {
29081         Roo.get(document).on('mouseover', this.enter ,this);
29082         Roo.get(document).on('mouseout', this.leave, this);
29083          
29084         
29085         this.currentTip = new Roo.bootstrap.Tooltip();
29086     },
29087     
29088     enter : function(ev)
29089     {
29090         var dom = ev.getTarget();
29091         
29092         //Roo.log(['enter',dom]);
29093         var el = Roo.fly(dom);
29094         if (this.currentEl) {
29095             //Roo.log(dom);
29096             //Roo.log(this.currentEl);
29097             //Roo.log(this.currentEl.contains(dom));
29098             if (this.currentEl == el) {
29099                 return;
29100             }
29101             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29102                 return;
29103             }
29104
29105         }
29106         
29107         if (this.currentTip.el) {
29108             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29109         }    
29110         //Roo.log(ev);
29111         
29112         if(!el || el.dom == document){
29113             return;
29114         }
29115         
29116         var bindEl = el; 
29117         var pel = false;
29118         if (!el.attr('tooltip')) {
29119             pel = el.findParent("[tooltip]");
29120             if (pel) {
29121                 bindEl = Roo.get(pel);
29122             }
29123         }
29124         
29125        
29126         
29127         // you can not look for children, as if el is the body.. then everythign is the child..
29128         if (!pel && !el.attr('tooltip')) { //
29129             if (!el.select("[tooltip]").elements.length) {
29130                 return;
29131             }
29132             // is the mouse over this child...?
29133             bindEl = el.select("[tooltip]").first();
29134             var xy = ev.getXY();
29135             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29136                 //Roo.log("not in region.");
29137                 return;
29138             }
29139             //Roo.log("child element over..");
29140             
29141         }
29142         this.currentEl = el;
29143         this.currentTip.bind(bindEl);
29144         this.currentRegion = Roo.lib.Region.getRegion(dom);
29145         this.currentTip.enter();
29146         
29147     },
29148     leave : function(ev)
29149     {
29150         var dom = ev.getTarget();
29151         //Roo.log(['leave',dom]);
29152         if (!this.currentEl) {
29153             return;
29154         }
29155         
29156         
29157         if (dom != this.currentEl.dom) {
29158             return;
29159         }
29160         var xy = ev.getXY();
29161         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29162             return;
29163         }
29164         // only activate leave if mouse cursor is outside... bounding box..
29165         
29166         
29167         
29168         
29169         if (this.currentTip) {
29170             this.currentTip.leave();
29171         }
29172         //Roo.log('clear currentEl');
29173         this.currentEl = false;
29174         
29175         
29176     },
29177     alignment : {
29178         'left' : ['r-l', [-2,0], 'right'],
29179         'right' : ['l-r', [2,0], 'left'],
29180         'bottom' : ['t-b', [0,2], 'top'],
29181         'top' : [ 'b-t', [0,-2], 'bottom']
29182     }
29183     
29184 });
29185
29186
29187 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29188     
29189     
29190     bindEl : false,
29191     
29192     delay : null, // can be { show : 300 , hide: 500}
29193     
29194     timeout : null,
29195     
29196     hoverState : null, //???
29197     
29198     placement : 'bottom', 
29199     
29200     alignment : false,
29201     
29202     getAutoCreate : function(){
29203     
29204         var cfg = {
29205            cls : 'tooltip',   
29206            role : 'tooltip',
29207            cn : [
29208                 {
29209                     cls : 'tooltip-arrow arrow'
29210                 },
29211                 {
29212                     cls : 'tooltip-inner'
29213                 }
29214            ]
29215         };
29216         
29217         return cfg;
29218     },
29219     bind : function(el)
29220     {
29221         this.bindEl = el;
29222     },
29223     
29224     initEvents : function()
29225     {
29226         this.arrowEl = this.el.select('.arrow', true).first();
29227         this.innerEl = this.el.select('.tooltip-inner', true).first();
29228     },
29229     
29230     enter : function () {
29231        
29232         if (this.timeout != null) {
29233             clearTimeout(this.timeout);
29234         }
29235         
29236         this.hoverState = 'in';
29237          //Roo.log("enter - show");
29238         if (!this.delay || !this.delay.show) {
29239             this.show();
29240             return;
29241         }
29242         var _t = this;
29243         this.timeout = setTimeout(function () {
29244             if (_t.hoverState == 'in') {
29245                 _t.show();
29246             }
29247         }, this.delay.show);
29248     },
29249     leave : function()
29250     {
29251         clearTimeout(this.timeout);
29252     
29253         this.hoverState = 'out';
29254          if (!this.delay || !this.delay.hide) {
29255             this.hide();
29256             return;
29257         }
29258        
29259         var _t = this;
29260         this.timeout = setTimeout(function () {
29261             //Roo.log("leave - timeout");
29262             
29263             if (_t.hoverState == 'out') {
29264                 _t.hide();
29265                 Roo.bootstrap.Tooltip.currentEl = false;
29266             }
29267         }, delay);
29268     },
29269     
29270     show : function (msg)
29271     {
29272         if (!this.el) {
29273             this.render(document.body);
29274         }
29275         // set content.
29276         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29277         
29278         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29279         
29280         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29281         
29282         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29283                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29284         
29285         var placement = typeof this.placement == 'function' ?
29286             this.placement.call(this, this.el, on_el) :
29287             this.placement;
29288             
29289         var autoToken = /\s?auto?\s?/i;
29290         var autoPlace = autoToken.test(placement);
29291         if (autoPlace) {
29292             placement = placement.replace(autoToken, '') || 'top';
29293         }
29294         
29295         //this.el.detach()
29296         //this.el.setXY([0,0]);
29297         this.el.show();
29298         //this.el.dom.style.display='block';
29299         
29300         //this.el.appendTo(on_el);
29301         
29302         var p = this.getPosition();
29303         var box = this.el.getBox();
29304         
29305         if (autoPlace) {
29306             // fixme..
29307         }
29308         
29309         var align = this.alignment[placement];
29310         
29311         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29312         
29313         if(placement == 'top' || placement == 'bottom'){
29314             if(xy[0] < 0){
29315                 placement = 'right';
29316             }
29317             
29318             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29319                 placement = 'left';
29320             }
29321             
29322             var scroll = Roo.select('body', true).first().getScroll();
29323             
29324             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29325                 placement = 'top';
29326             }
29327             
29328             align = this.alignment[placement];
29329             
29330             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29331             
29332         }
29333         
29334         var elems = document.getElementsByTagName('div');
29335         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29336         for (var i = 0; i < elems.length; i++) {
29337           var zindex = Number.parseInt(
29338                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29339                 10
29340           );
29341           if (zindex > highest) {
29342             highest = zindex;
29343           }
29344         }
29345         
29346         
29347         
29348         this.el.dom.style.zIndex = highest;
29349         
29350         this.el.alignTo(this.bindEl, align[0],align[1]);
29351         //var arrow = this.el.select('.arrow',true).first();
29352         //arrow.set(align[2], 
29353         
29354         this.el.addClass(placement);
29355         this.el.addClass("bs-tooltip-"+ placement);
29356         
29357         this.el.addClass('in fade show');
29358         
29359         this.hoverState = null;
29360         
29361         if (this.el.hasClass('fade')) {
29362             // fade it?
29363         }
29364         
29365         
29366         
29367         
29368         
29369     },
29370     hide : function()
29371     {
29372          
29373         if (!this.el) {
29374             return;
29375         }
29376         //this.el.setXY([0,0]);
29377         this.el.removeClass(['show', 'in']);
29378         //this.el.hide();
29379         
29380     }
29381     
29382 });
29383  
29384
29385  /*
29386  * - LGPL
29387  *
29388  * Location Picker
29389  * 
29390  */
29391
29392 /**
29393  * @class Roo.bootstrap.LocationPicker
29394  * @extends Roo.bootstrap.Component
29395  * Bootstrap LocationPicker class
29396  * @cfg {Number} latitude Position when init default 0
29397  * @cfg {Number} longitude Position when init default 0
29398  * @cfg {Number} zoom default 15
29399  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29400  * @cfg {Boolean} mapTypeControl default false
29401  * @cfg {Boolean} disableDoubleClickZoom default false
29402  * @cfg {Boolean} scrollwheel default true
29403  * @cfg {Boolean} streetViewControl default false
29404  * @cfg {Number} radius default 0
29405  * @cfg {String} locationName
29406  * @cfg {Boolean} draggable default true
29407  * @cfg {Boolean} enableAutocomplete default false
29408  * @cfg {Boolean} enableReverseGeocode default true
29409  * @cfg {String} markerTitle
29410  * 
29411  * @constructor
29412  * Create a new LocationPicker
29413  * @param {Object} config The config object
29414  */
29415
29416
29417 Roo.bootstrap.LocationPicker = function(config){
29418     
29419     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29420     
29421     this.addEvents({
29422         /**
29423          * @event initial
29424          * Fires when the picker initialized.
29425          * @param {Roo.bootstrap.LocationPicker} this
29426          * @param {Google Location} location
29427          */
29428         initial : true,
29429         /**
29430          * @event positionchanged
29431          * Fires when the picker position changed.
29432          * @param {Roo.bootstrap.LocationPicker} this
29433          * @param {Google Location} location
29434          */
29435         positionchanged : true,
29436         /**
29437          * @event resize
29438          * Fires when the map resize.
29439          * @param {Roo.bootstrap.LocationPicker} this
29440          */
29441         resize : true,
29442         /**
29443          * @event show
29444          * Fires when the map show.
29445          * @param {Roo.bootstrap.LocationPicker} this
29446          */
29447         show : true,
29448         /**
29449          * @event hide
29450          * Fires when the map hide.
29451          * @param {Roo.bootstrap.LocationPicker} this
29452          */
29453         hide : true,
29454         /**
29455          * @event mapClick
29456          * Fires when click the map.
29457          * @param {Roo.bootstrap.LocationPicker} this
29458          * @param {Map event} e
29459          */
29460         mapClick : true,
29461         /**
29462          * @event mapRightClick
29463          * Fires when right click the map.
29464          * @param {Roo.bootstrap.LocationPicker} this
29465          * @param {Map event} e
29466          */
29467         mapRightClick : true,
29468         /**
29469          * @event markerClick
29470          * Fires when click the marker.
29471          * @param {Roo.bootstrap.LocationPicker} this
29472          * @param {Map event} e
29473          */
29474         markerClick : true,
29475         /**
29476          * @event markerRightClick
29477          * Fires when right click the marker.
29478          * @param {Roo.bootstrap.LocationPicker} this
29479          * @param {Map event} e
29480          */
29481         markerRightClick : true,
29482         /**
29483          * @event OverlayViewDraw
29484          * Fires when OverlayView Draw
29485          * @param {Roo.bootstrap.LocationPicker} this
29486          */
29487         OverlayViewDraw : true,
29488         /**
29489          * @event OverlayViewOnAdd
29490          * Fires when OverlayView Draw
29491          * @param {Roo.bootstrap.LocationPicker} this
29492          */
29493         OverlayViewOnAdd : true,
29494         /**
29495          * @event OverlayViewOnRemove
29496          * Fires when OverlayView Draw
29497          * @param {Roo.bootstrap.LocationPicker} this
29498          */
29499         OverlayViewOnRemove : true,
29500         /**
29501          * @event OverlayViewShow
29502          * Fires when OverlayView Draw
29503          * @param {Roo.bootstrap.LocationPicker} this
29504          * @param {Pixel} cpx
29505          */
29506         OverlayViewShow : true,
29507         /**
29508          * @event OverlayViewHide
29509          * Fires when OverlayView Draw
29510          * @param {Roo.bootstrap.LocationPicker} this
29511          */
29512         OverlayViewHide : true,
29513         /**
29514          * @event loadexception
29515          * Fires when load google lib failed.
29516          * @param {Roo.bootstrap.LocationPicker} this
29517          */
29518         loadexception : true
29519     });
29520         
29521 };
29522
29523 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29524     
29525     gMapContext: false,
29526     
29527     latitude: 0,
29528     longitude: 0,
29529     zoom: 15,
29530     mapTypeId: false,
29531     mapTypeControl: false,
29532     disableDoubleClickZoom: false,
29533     scrollwheel: true,
29534     streetViewControl: false,
29535     radius: 0,
29536     locationName: '',
29537     draggable: true,
29538     enableAutocomplete: false,
29539     enableReverseGeocode: true,
29540     markerTitle: '',
29541     
29542     getAutoCreate: function()
29543     {
29544
29545         var cfg = {
29546             tag: 'div',
29547             cls: 'roo-location-picker'
29548         };
29549         
29550         return cfg
29551     },
29552     
29553     initEvents: function(ct, position)
29554     {       
29555         if(!this.el.getWidth() || this.isApplied()){
29556             return;
29557         }
29558         
29559         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29560         
29561         this.initial();
29562     },
29563     
29564     initial: function()
29565     {
29566         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29567             this.fireEvent('loadexception', this);
29568             return;
29569         }
29570         
29571         if(!this.mapTypeId){
29572             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29573         }
29574         
29575         this.gMapContext = this.GMapContext();
29576         
29577         this.initOverlayView();
29578         
29579         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29580         
29581         var _this = this;
29582                 
29583         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29584             _this.setPosition(_this.gMapContext.marker.position);
29585         });
29586         
29587         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29588             _this.fireEvent('mapClick', this, event);
29589             
29590         });
29591
29592         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29593             _this.fireEvent('mapRightClick', this, event);
29594             
29595         });
29596         
29597         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29598             _this.fireEvent('markerClick', this, event);
29599             
29600         });
29601
29602         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29603             _this.fireEvent('markerRightClick', this, event);
29604             
29605         });
29606         
29607         this.setPosition(this.gMapContext.location);
29608         
29609         this.fireEvent('initial', this, this.gMapContext.location);
29610     },
29611     
29612     initOverlayView: function()
29613     {
29614         var _this = this;
29615         
29616         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29617             
29618             draw: function()
29619             {
29620                 _this.fireEvent('OverlayViewDraw', _this);
29621             },
29622             
29623             onAdd: function()
29624             {
29625                 _this.fireEvent('OverlayViewOnAdd', _this);
29626             },
29627             
29628             onRemove: function()
29629             {
29630                 _this.fireEvent('OverlayViewOnRemove', _this);
29631             },
29632             
29633             show: function(cpx)
29634             {
29635                 _this.fireEvent('OverlayViewShow', _this, cpx);
29636             },
29637             
29638             hide: function()
29639             {
29640                 _this.fireEvent('OverlayViewHide', _this);
29641             }
29642             
29643         });
29644     },
29645     
29646     fromLatLngToContainerPixel: function(event)
29647     {
29648         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29649     },
29650     
29651     isApplied: function() 
29652     {
29653         return this.getGmapContext() == false ? false : true;
29654     },
29655     
29656     getGmapContext: function() 
29657     {
29658         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29659     },
29660     
29661     GMapContext: function() 
29662     {
29663         var position = new google.maps.LatLng(this.latitude, this.longitude);
29664         
29665         var _map = new google.maps.Map(this.el.dom, {
29666             center: position,
29667             zoom: this.zoom,
29668             mapTypeId: this.mapTypeId,
29669             mapTypeControl: this.mapTypeControl,
29670             disableDoubleClickZoom: this.disableDoubleClickZoom,
29671             scrollwheel: this.scrollwheel,
29672             streetViewControl: this.streetViewControl,
29673             locationName: this.locationName,
29674             draggable: this.draggable,
29675             enableAutocomplete: this.enableAutocomplete,
29676             enableReverseGeocode: this.enableReverseGeocode
29677         });
29678         
29679         var _marker = new google.maps.Marker({
29680             position: position,
29681             map: _map,
29682             title: this.markerTitle,
29683             draggable: this.draggable
29684         });
29685         
29686         return {
29687             map: _map,
29688             marker: _marker,
29689             circle: null,
29690             location: position,
29691             radius: this.radius,
29692             locationName: this.locationName,
29693             addressComponents: {
29694                 formatted_address: null,
29695                 addressLine1: null,
29696                 addressLine2: null,
29697                 streetName: null,
29698                 streetNumber: null,
29699                 city: null,
29700                 district: null,
29701                 state: null,
29702                 stateOrProvince: null
29703             },
29704             settings: this,
29705             domContainer: this.el.dom,
29706             geodecoder: new google.maps.Geocoder()
29707         };
29708     },
29709     
29710     drawCircle: function(center, radius, options) 
29711     {
29712         if (this.gMapContext.circle != null) {
29713             this.gMapContext.circle.setMap(null);
29714         }
29715         if (radius > 0) {
29716             radius *= 1;
29717             options = Roo.apply({}, options, {
29718                 strokeColor: "#0000FF",
29719                 strokeOpacity: .35,
29720                 strokeWeight: 2,
29721                 fillColor: "#0000FF",
29722                 fillOpacity: .2
29723             });
29724             
29725             options.map = this.gMapContext.map;
29726             options.radius = radius;
29727             options.center = center;
29728             this.gMapContext.circle = new google.maps.Circle(options);
29729             return this.gMapContext.circle;
29730         }
29731         
29732         return null;
29733     },
29734     
29735     setPosition: function(location) 
29736     {
29737         this.gMapContext.location = location;
29738         this.gMapContext.marker.setPosition(location);
29739         this.gMapContext.map.panTo(location);
29740         this.drawCircle(location, this.gMapContext.radius, {});
29741         
29742         var _this = this;
29743         
29744         if (this.gMapContext.settings.enableReverseGeocode) {
29745             this.gMapContext.geodecoder.geocode({
29746                 latLng: this.gMapContext.location
29747             }, function(results, status) {
29748                 
29749                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29750                     _this.gMapContext.locationName = results[0].formatted_address;
29751                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29752                     
29753                     _this.fireEvent('positionchanged', this, location);
29754                 }
29755             });
29756             
29757             return;
29758         }
29759         
29760         this.fireEvent('positionchanged', this, location);
29761     },
29762     
29763     resize: function()
29764     {
29765         google.maps.event.trigger(this.gMapContext.map, "resize");
29766         
29767         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29768         
29769         this.fireEvent('resize', this);
29770     },
29771     
29772     setPositionByLatLng: function(latitude, longitude)
29773     {
29774         this.setPosition(new google.maps.LatLng(latitude, longitude));
29775     },
29776     
29777     getCurrentPosition: function() 
29778     {
29779         return {
29780             latitude: this.gMapContext.location.lat(),
29781             longitude: this.gMapContext.location.lng()
29782         };
29783     },
29784     
29785     getAddressName: function() 
29786     {
29787         return this.gMapContext.locationName;
29788     },
29789     
29790     getAddressComponents: function() 
29791     {
29792         return this.gMapContext.addressComponents;
29793     },
29794     
29795     address_component_from_google_geocode: function(address_components) 
29796     {
29797         var result = {};
29798         
29799         for (var i = 0; i < address_components.length; i++) {
29800             var component = address_components[i];
29801             if (component.types.indexOf("postal_code") >= 0) {
29802                 result.postalCode = component.short_name;
29803             } else if (component.types.indexOf("street_number") >= 0) {
29804                 result.streetNumber = component.short_name;
29805             } else if (component.types.indexOf("route") >= 0) {
29806                 result.streetName = component.short_name;
29807             } else if (component.types.indexOf("neighborhood") >= 0) {
29808                 result.city = component.short_name;
29809             } else if (component.types.indexOf("locality") >= 0) {
29810                 result.city = component.short_name;
29811             } else if (component.types.indexOf("sublocality") >= 0) {
29812                 result.district = component.short_name;
29813             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29814                 result.stateOrProvince = component.short_name;
29815             } else if (component.types.indexOf("country") >= 0) {
29816                 result.country = component.short_name;
29817             }
29818         }
29819         
29820         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29821         result.addressLine2 = "";
29822         return result;
29823     },
29824     
29825     setZoomLevel: function(zoom)
29826     {
29827         this.gMapContext.map.setZoom(zoom);
29828     },
29829     
29830     show: function()
29831     {
29832         if(!this.el){
29833             return;
29834         }
29835         
29836         this.el.show();
29837         
29838         this.resize();
29839         
29840         this.fireEvent('show', this);
29841     },
29842     
29843     hide: function()
29844     {
29845         if(!this.el){
29846             return;
29847         }
29848         
29849         this.el.hide();
29850         
29851         this.fireEvent('hide', this);
29852     }
29853     
29854 });
29855
29856 Roo.apply(Roo.bootstrap.LocationPicker, {
29857     
29858     OverlayView : function(map, options)
29859     {
29860         options = options || {};
29861         
29862         this.setMap(map);
29863     }
29864     
29865     
29866 });/**
29867  * @class Roo.bootstrap.Alert
29868  * @extends Roo.bootstrap.Component
29869  * Bootstrap Alert class - shows an alert area box
29870  * eg
29871  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29872   Enter a valid email address
29873 </div>
29874  * @licence LGPL
29875  * @cfg {String} title The title of alert
29876  * @cfg {String} html The content of alert
29877  * @cfg {String} weight (  success | info | warning | danger )
29878  * @cfg {String} fa font-awesomeicon
29879  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29880  * @cfg {Boolean} close true to show a x closer
29881  * 
29882  * 
29883  * @constructor
29884  * Create a new alert
29885  * @param {Object} config The config object
29886  */
29887
29888
29889 Roo.bootstrap.Alert = function(config){
29890     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29891     
29892 };
29893
29894 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29895     
29896     title: '',
29897     html: '',
29898     weight: false,
29899     fa: false,
29900     faicon: false, // BC
29901     close : false,
29902     
29903     
29904     getAutoCreate : function()
29905     {
29906         
29907         var cfg = {
29908             tag : 'div',
29909             cls : 'alert',
29910             cn : [
29911                 {
29912                     tag: 'button',
29913                     type :  "button",
29914                     cls: "close",
29915                     html : '×',
29916                     style : this.close ? '' : 'display:none'
29917                 },
29918                 {
29919                     tag : 'i',
29920                     cls : 'roo-alert-icon'
29921                     
29922                 },
29923                 {
29924                     tag : 'b',
29925                     cls : 'roo-alert-title',
29926                     html : this.title
29927                 },
29928                 {
29929                     tag : 'span',
29930                     cls : 'roo-alert-text',
29931                     html : this.html
29932                 }
29933             ]
29934         };
29935         
29936         if(this.faicon){
29937             cfg.cn[0].cls += ' fa ' + this.faicon;
29938         }
29939         if(this.fa){
29940             cfg.cn[0].cls += ' fa ' + this.fa;
29941         }
29942         
29943         if(this.weight){
29944             cfg.cls += ' alert-' + this.weight;
29945         }
29946         
29947         return cfg;
29948     },
29949     
29950     initEvents: function() 
29951     {
29952         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29953         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29954         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29955         if (this.seconds > 0) {
29956             this.hide.defer(this.seconds, this);
29957         }
29958     },
29959     
29960     setTitle : function(str)
29961     {
29962         this.titleEl.dom.innerHTML = str;
29963     },
29964     
29965     setText : function(str)
29966     {
29967         this.titleEl.dom.innerHTML = str;
29968     },
29969     
29970     setWeight : function(weight)
29971     {
29972         if(this.weight){
29973             this.el.removeClass('alert-' + this.weight);
29974         }
29975         
29976         this.weight = weight;
29977         
29978         this.el.addClass('alert-' + this.weight);
29979     },
29980     
29981     setIcon : function(icon)
29982     {
29983         if(this.faicon){
29984             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29985         }
29986         
29987         this.faicon = icon;
29988         
29989         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29990     },
29991     
29992     hide: function() 
29993     {
29994         this.el.hide();   
29995     },
29996     
29997     show: function() 
29998     {  
29999         this.el.show();   
30000     }
30001     
30002 });
30003
30004  
30005 /*
30006 * Licence: LGPL
30007 */
30008
30009 /**
30010  * @class Roo.bootstrap.UploadCropbox
30011  * @extends Roo.bootstrap.Component
30012  * Bootstrap UploadCropbox class
30013  * @cfg {String} emptyText show when image has been loaded
30014  * @cfg {String} rotateNotify show when image too small to rotate
30015  * @cfg {Number} errorTimeout default 3000
30016  * @cfg {Number} minWidth default 300
30017  * @cfg {Number} minHeight default 300
30018  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30019  * @cfg {Boolean} isDocument (true|false) default false
30020  * @cfg {String} url action url
30021  * @cfg {String} paramName default 'imageUpload'
30022  * @cfg {String} method default POST
30023  * @cfg {Boolean} loadMask (true|false) default true
30024  * @cfg {Boolean} loadingText default 'Loading...'
30025  * 
30026  * @constructor
30027  * Create a new UploadCropbox
30028  * @param {Object} config The config object
30029  */
30030
30031 Roo.bootstrap.UploadCropbox = function(config){
30032     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30033     
30034     this.addEvents({
30035         /**
30036          * @event beforeselectfile
30037          * Fire before select file
30038          * @param {Roo.bootstrap.UploadCropbox} this
30039          */
30040         "beforeselectfile" : true,
30041         /**
30042          * @event initial
30043          * Fire after initEvent
30044          * @param {Roo.bootstrap.UploadCropbox} this
30045          */
30046         "initial" : true,
30047         /**
30048          * @event crop
30049          * Fire after initEvent
30050          * @param {Roo.bootstrap.UploadCropbox} this
30051          * @param {String} data
30052          */
30053         "crop" : true,
30054         /**
30055          * @event prepare
30056          * Fire when preparing the file data
30057          * @param {Roo.bootstrap.UploadCropbox} this
30058          * @param {Object} file
30059          */
30060         "prepare" : true,
30061         /**
30062          * @event exception
30063          * Fire when get exception
30064          * @param {Roo.bootstrap.UploadCropbox} this
30065          * @param {XMLHttpRequest} xhr
30066          */
30067         "exception" : true,
30068         /**
30069          * @event beforeloadcanvas
30070          * Fire before load the canvas
30071          * @param {Roo.bootstrap.UploadCropbox} this
30072          * @param {String} src
30073          */
30074         "beforeloadcanvas" : true,
30075         /**
30076          * @event trash
30077          * Fire when trash image
30078          * @param {Roo.bootstrap.UploadCropbox} this
30079          */
30080         "trash" : true,
30081         /**
30082          * @event download
30083          * Fire when download the image
30084          * @param {Roo.bootstrap.UploadCropbox} this
30085          */
30086         "download" : true,
30087         /**
30088          * @event footerbuttonclick
30089          * Fire when footerbuttonclick
30090          * @param {Roo.bootstrap.UploadCropbox} this
30091          * @param {String} type
30092          */
30093         "footerbuttonclick" : true,
30094         /**
30095          * @event resize
30096          * Fire when resize
30097          * @param {Roo.bootstrap.UploadCropbox} this
30098          */
30099         "resize" : true,
30100         /**
30101          * @event rotate
30102          * Fire when rotate the image
30103          * @param {Roo.bootstrap.UploadCropbox} this
30104          * @param {String} pos
30105          */
30106         "rotate" : true,
30107         /**
30108          * @event inspect
30109          * Fire when inspect the file
30110          * @param {Roo.bootstrap.UploadCropbox} this
30111          * @param {Object} file
30112          */
30113         "inspect" : true,
30114         /**
30115          * @event upload
30116          * Fire when xhr upload the file
30117          * @param {Roo.bootstrap.UploadCropbox} this
30118          * @param {Object} data
30119          */
30120         "upload" : true,
30121         /**
30122          * @event arrange
30123          * Fire when arrange the file data
30124          * @param {Roo.bootstrap.UploadCropbox} this
30125          * @param {Object} formData
30126          */
30127         "arrange" : true
30128     });
30129     
30130     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30131 };
30132
30133 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30134     
30135     emptyText : 'Click to upload image',
30136     rotateNotify : 'Image is too small to rotate',
30137     errorTimeout : 3000,
30138     scale : 0,
30139     baseScale : 1,
30140     rotate : 0,
30141     dragable : false,
30142     pinching : false,
30143     mouseX : 0,
30144     mouseY : 0,
30145     cropData : false,
30146     minWidth : 300,
30147     minHeight : 300,
30148     file : false,
30149     exif : {},
30150     baseRotate : 1,
30151     cropType : 'image/jpeg',
30152     buttons : false,
30153     canvasLoaded : false,
30154     isDocument : false,
30155     method : 'POST',
30156     paramName : 'imageUpload',
30157     loadMask : true,
30158     loadingText : 'Loading...',
30159     maskEl : false,
30160     
30161     getAutoCreate : function()
30162     {
30163         var cfg = {
30164             tag : 'div',
30165             cls : 'roo-upload-cropbox',
30166             cn : [
30167                 {
30168                     tag : 'input',
30169                     cls : 'roo-upload-cropbox-selector',
30170                     type : 'file'
30171                 },
30172                 {
30173                     tag : 'div',
30174                     cls : 'roo-upload-cropbox-body',
30175                     style : 'cursor:pointer',
30176                     cn : [
30177                         {
30178                             tag : 'div',
30179                             cls : 'roo-upload-cropbox-preview'
30180                         },
30181                         {
30182                             tag : 'div',
30183                             cls : 'roo-upload-cropbox-thumb'
30184                         },
30185                         {
30186                             tag : 'div',
30187                             cls : 'roo-upload-cropbox-empty-notify',
30188                             html : this.emptyText
30189                         },
30190                         {
30191                             tag : 'div',
30192                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30193                             html : this.rotateNotify
30194                         }
30195                     ]
30196                 },
30197                 {
30198                     tag : 'div',
30199                     cls : 'roo-upload-cropbox-footer',
30200                     cn : {
30201                         tag : 'div',
30202                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30203                         cn : []
30204                     }
30205                 }
30206             ]
30207         };
30208         
30209         return cfg;
30210     },
30211     
30212     onRender : function(ct, position)
30213     {
30214         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30215         
30216         if (this.buttons.length) {
30217             
30218             Roo.each(this.buttons, function(bb) {
30219                 
30220                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30221                 
30222                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30223                 
30224             }, this);
30225         }
30226         
30227         if(this.loadMask){
30228             this.maskEl = this.el;
30229         }
30230     },
30231     
30232     initEvents : function()
30233     {
30234         this.urlAPI = (window.createObjectURL && window) || 
30235                                 (window.URL && URL.revokeObjectURL && URL) || 
30236                                 (window.webkitURL && webkitURL);
30237                         
30238         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30239         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240         
30241         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30242         this.selectorEl.hide();
30243         
30244         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30245         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30246         
30247         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30248         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30249         this.thumbEl.hide();
30250         
30251         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30252         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30253         
30254         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30255         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30256         this.errorEl.hide();
30257         
30258         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30259         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30260         this.footerEl.hide();
30261         
30262         this.setThumbBoxSize();
30263         
30264         this.bind();
30265         
30266         this.resize();
30267         
30268         this.fireEvent('initial', this);
30269     },
30270
30271     bind : function()
30272     {
30273         var _this = this;
30274         
30275         window.addEventListener("resize", function() { _this.resize(); } );
30276         
30277         this.bodyEl.on('click', this.beforeSelectFile, this);
30278         
30279         if(Roo.isTouch){
30280             this.bodyEl.on('touchstart', this.onTouchStart, this);
30281             this.bodyEl.on('touchmove', this.onTouchMove, this);
30282             this.bodyEl.on('touchend', this.onTouchEnd, this);
30283         }
30284         
30285         if(!Roo.isTouch){
30286             this.bodyEl.on('mousedown', this.onMouseDown, this);
30287             this.bodyEl.on('mousemove', this.onMouseMove, this);
30288             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30289             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30290             Roo.get(document).on('mouseup', this.onMouseUp, this);
30291         }
30292         
30293         this.selectorEl.on('change', this.onFileSelected, this);
30294     },
30295     
30296     reset : function()
30297     {    
30298         this.scale = 0;
30299         this.baseScale = 1;
30300         this.rotate = 0;
30301         this.baseRotate = 1;
30302         this.dragable = false;
30303         this.pinching = false;
30304         this.mouseX = 0;
30305         this.mouseY = 0;
30306         this.cropData = false;
30307         this.notifyEl.dom.innerHTML = this.emptyText;
30308         
30309         this.selectorEl.dom.value = '';
30310         
30311     },
30312     
30313     resize : function()
30314     {
30315         if(this.fireEvent('resize', this) != false){
30316             this.setThumbBoxPosition();
30317             this.setCanvasPosition();
30318         }
30319     },
30320     
30321     onFooterButtonClick : function(e, el, o, type)
30322     {
30323         switch (type) {
30324             case 'rotate-left' :
30325                 this.onRotateLeft(e);
30326                 break;
30327             case 'rotate-right' :
30328                 this.onRotateRight(e);
30329                 break;
30330             case 'picture' :
30331                 this.beforeSelectFile(e);
30332                 break;
30333             case 'trash' :
30334                 this.trash(e);
30335                 break;
30336             case 'crop' :
30337                 this.crop(e);
30338                 break;
30339             case 'download' :
30340                 this.download(e);
30341                 break;
30342             default :
30343                 break;
30344         }
30345         
30346         this.fireEvent('footerbuttonclick', this, type);
30347     },
30348     
30349     beforeSelectFile : function(e)
30350     {
30351         e.preventDefault();
30352         
30353         if(this.fireEvent('beforeselectfile', this) != false){
30354             this.selectorEl.dom.click();
30355         }
30356     },
30357     
30358     onFileSelected : function(e)
30359     {
30360         e.preventDefault();
30361         
30362         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30363             return;
30364         }
30365         
30366         var file = this.selectorEl.dom.files[0];
30367         
30368         if(this.fireEvent('inspect', this, file) != false){
30369             this.prepare(file);
30370         }
30371         
30372     },
30373     
30374     trash : function(e)
30375     {
30376         this.fireEvent('trash', this);
30377     },
30378     
30379     download : function(e)
30380     {
30381         this.fireEvent('download', this);
30382     },
30383     
30384     loadCanvas : function(src)
30385     {   
30386         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30387             
30388             this.reset();
30389             
30390             this.imageEl = document.createElement('img');
30391             
30392             var _this = this;
30393             
30394             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30395             
30396             this.imageEl.src = src;
30397         }
30398     },
30399     
30400     onLoadCanvas : function()
30401     {   
30402         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30403         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30404         
30405         this.bodyEl.un('click', this.beforeSelectFile, this);
30406         
30407         this.notifyEl.hide();
30408         this.thumbEl.show();
30409         this.footerEl.show();
30410         
30411         this.baseRotateLevel();
30412         
30413         if(this.isDocument){
30414             this.setThumbBoxSize();
30415         }
30416         
30417         this.setThumbBoxPosition();
30418         
30419         this.baseScaleLevel();
30420         
30421         this.draw();
30422         
30423         this.resize();
30424         
30425         this.canvasLoaded = true;
30426         
30427         if(this.loadMask){
30428             this.maskEl.unmask();
30429         }
30430         
30431     },
30432     
30433     setCanvasPosition : function()
30434     {   
30435         if(!this.canvasEl){
30436             return;
30437         }
30438         
30439         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30440         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30441         
30442         this.previewEl.setLeft(pw);
30443         this.previewEl.setTop(ph);
30444         
30445     },
30446     
30447     onMouseDown : function(e)
30448     {   
30449         e.stopEvent();
30450         
30451         this.dragable = true;
30452         this.pinching = false;
30453         
30454         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30455             this.dragable = false;
30456             return;
30457         }
30458         
30459         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30460         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30461         
30462     },
30463     
30464     onMouseMove : function(e)
30465     {   
30466         e.stopEvent();
30467         
30468         if(!this.canvasLoaded){
30469             return;
30470         }
30471         
30472         if (!this.dragable){
30473             return;
30474         }
30475         
30476         var minX = Math.ceil(this.thumbEl.getLeft(true));
30477         var minY = Math.ceil(this.thumbEl.getTop(true));
30478         
30479         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30480         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30481         
30482         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30483         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30484         
30485         x = x - this.mouseX;
30486         y = y - this.mouseY;
30487         
30488         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30489         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30490         
30491         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30492         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30493         
30494         this.previewEl.setLeft(bgX);
30495         this.previewEl.setTop(bgY);
30496         
30497         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30498         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30499     },
30500     
30501     onMouseUp : function(e)
30502     {   
30503         e.stopEvent();
30504         
30505         this.dragable = false;
30506     },
30507     
30508     onMouseWheel : function(e)
30509     {   
30510         e.stopEvent();
30511         
30512         this.startScale = this.scale;
30513         
30514         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30515         
30516         if(!this.zoomable()){
30517             this.scale = this.startScale;
30518             return;
30519         }
30520         
30521         this.draw();
30522         
30523         return;
30524     },
30525     
30526     zoomable : function()
30527     {
30528         var minScale = this.thumbEl.getWidth() / this.minWidth;
30529         
30530         if(this.minWidth < this.minHeight){
30531             minScale = this.thumbEl.getHeight() / this.minHeight;
30532         }
30533         
30534         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30535         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30536         
30537         if(
30538                 this.isDocument &&
30539                 (this.rotate == 0 || this.rotate == 180) && 
30540                 (
30541                     width > this.imageEl.OriginWidth || 
30542                     height > this.imageEl.OriginHeight ||
30543                     (width < this.minWidth && height < this.minHeight)
30544                 )
30545         ){
30546             return false;
30547         }
30548         
30549         if(
30550                 this.isDocument &&
30551                 (this.rotate == 90 || this.rotate == 270) && 
30552                 (
30553                     width > this.imageEl.OriginWidth || 
30554                     height > this.imageEl.OriginHeight ||
30555                     (width < this.minHeight && height < this.minWidth)
30556                 )
30557         ){
30558             return false;
30559         }
30560         
30561         if(
30562                 !this.isDocument &&
30563                 (this.rotate == 0 || this.rotate == 180) && 
30564                 (
30565                     width < this.minWidth || 
30566                     width > this.imageEl.OriginWidth || 
30567                     height < this.minHeight || 
30568                     height > this.imageEl.OriginHeight
30569                 )
30570         ){
30571             return false;
30572         }
30573         
30574         if(
30575                 !this.isDocument &&
30576                 (this.rotate == 90 || this.rotate == 270) && 
30577                 (
30578                     width < this.minHeight || 
30579                     width > this.imageEl.OriginWidth || 
30580                     height < this.minWidth || 
30581                     height > this.imageEl.OriginHeight
30582                 )
30583         ){
30584             return false;
30585         }
30586         
30587         return true;
30588         
30589     },
30590     
30591     onRotateLeft : function(e)
30592     {   
30593         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30594             
30595             var minScale = this.thumbEl.getWidth() / this.minWidth;
30596             
30597             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30598             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30599             
30600             this.startScale = this.scale;
30601             
30602             while (this.getScaleLevel() < minScale){
30603             
30604                 this.scale = this.scale + 1;
30605                 
30606                 if(!this.zoomable()){
30607                     break;
30608                 }
30609                 
30610                 if(
30611                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30612                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30613                 ){
30614                     continue;
30615                 }
30616                 
30617                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30618
30619                 this.draw();
30620                 
30621                 return;
30622             }
30623             
30624             this.scale = this.startScale;
30625             
30626             this.onRotateFail();
30627             
30628             return false;
30629         }
30630         
30631         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30632
30633         if(this.isDocument){
30634             this.setThumbBoxSize();
30635             this.setThumbBoxPosition();
30636             this.setCanvasPosition();
30637         }
30638         
30639         this.draw();
30640         
30641         this.fireEvent('rotate', this, 'left');
30642         
30643     },
30644     
30645     onRotateRight : function(e)
30646     {
30647         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30648             
30649             var minScale = this.thumbEl.getWidth() / this.minWidth;
30650         
30651             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30652             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30653             
30654             this.startScale = this.scale;
30655             
30656             while (this.getScaleLevel() < minScale){
30657             
30658                 this.scale = this.scale + 1;
30659                 
30660                 if(!this.zoomable()){
30661                     break;
30662                 }
30663                 
30664                 if(
30665                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30666                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30667                 ){
30668                     continue;
30669                 }
30670                 
30671                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30672
30673                 this.draw();
30674                 
30675                 return;
30676             }
30677             
30678             this.scale = this.startScale;
30679             
30680             this.onRotateFail();
30681             
30682             return false;
30683         }
30684         
30685         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30686
30687         if(this.isDocument){
30688             this.setThumbBoxSize();
30689             this.setThumbBoxPosition();
30690             this.setCanvasPosition();
30691         }
30692         
30693         this.draw();
30694         
30695         this.fireEvent('rotate', this, 'right');
30696     },
30697     
30698     onRotateFail : function()
30699     {
30700         this.errorEl.show(true);
30701         
30702         var _this = this;
30703         
30704         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30705     },
30706     
30707     draw : function()
30708     {
30709         this.previewEl.dom.innerHTML = '';
30710         
30711         var canvasEl = document.createElement("canvas");
30712         
30713         var contextEl = canvasEl.getContext("2d");
30714         
30715         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30716         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30717         var center = this.imageEl.OriginWidth / 2;
30718         
30719         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30720             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30721             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30722             center = this.imageEl.OriginHeight / 2;
30723         }
30724         
30725         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30726         
30727         contextEl.translate(center, center);
30728         contextEl.rotate(this.rotate * Math.PI / 180);
30729
30730         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30731         
30732         this.canvasEl = document.createElement("canvas");
30733         
30734         this.contextEl = this.canvasEl.getContext("2d");
30735         
30736         switch (this.rotate) {
30737             case 0 :
30738                 
30739                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30740                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30741                 
30742                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30743                 
30744                 break;
30745             case 90 : 
30746                 
30747                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30748                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30749                 
30750                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30751                     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);
30752                     break;
30753                 }
30754                 
30755                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30756                 
30757                 break;
30758             case 180 :
30759                 
30760                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30761                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30762                 
30763                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30764                     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);
30765                     break;
30766                 }
30767                 
30768                 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);
30769                 
30770                 break;
30771             case 270 :
30772                 
30773                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30774                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30775         
30776                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30777                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30778                     break;
30779                 }
30780                 
30781                 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);
30782                 
30783                 break;
30784             default : 
30785                 break;
30786         }
30787         
30788         this.previewEl.appendChild(this.canvasEl);
30789         
30790         this.setCanvasPosition();
30791     },
30792     
30793     crop : function()
30794     {
30795         if(!this.canvasLoaded){
30796             return;
30797         }
30798         
30799         var imageCanvas = document.createElement("canvas");
30800         
30801         var imageContext = imageCanvas.getContext("2d");
30802         
30803         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30804         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30805         
30806         var center = imageCanvas.width / 2;
30807         
30808         imageContext.translate(center, center);
30809         
30810         imageContext.rotate(this.rotate * Math.PI / 180);
30811         
30812         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30813         
30814         var canvas = document.createElement("canvas");
30815         
30816         var context = canvas.getContext("2d");
30817                 
30818         canvas.width = this.minWidth;
30819         canvas.height = this.minHeight;
30820
30821         switch (this.rotate) {
30822             case 0 :
30823                 
30824                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30825                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30826                 
30827                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30828                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30829                 
30830                 var targetWidth = this.minWidth - 2 * x;
30831                 var targetHeight = this.minHeight - 2 * y;
30832                 
30833                 var scale = 1;
30834                 
30835                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30836                     scale = targetWidth / width;
30837                 }
30838                 
30839                 if(x > 0 && y == 0){
30840                     scale = targetHeight / height;
30841                 }
30842                 
30843                 if(x > 0 && y > 0){
30844                     scale = targetWidth / width;
30845                     
30846                     if(width < height){
30847                         scale = targetHeight / height;
30848                     }
30849                 }
30850                 
30851                 context.scale(scale, scale);
30852                 
30853                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30854                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30855
30856                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30857                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30858
30859                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30860                 
30861                 break;
30862             case 90 : 
30863                 
30864                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30865                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30866                 
30867                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30868                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30869                 
30870                 var targetWidth = this.minWidth - 2 * x;
30871                 var targetHeight = this.minHeight - 2 * y;
30872                 
30873                 var scale = 1;
30874                 
30875                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30876                     scale = targetWidth / width;
30877                 }
30878                 
30879                 if(x > 0 && y == 0){
30880                     scale = targetHeight / height;
30881                 }
30882                 
30883                 if(x > 0 && y > 0){
30884                     scale = targetWidth / width;
30885                     
30886                     if(width < height){
30887                         scale = targetHeight / height;
30888                     }
30889                 }
30890                 
30891                 context.scale(scale, scale);
30892                 
30893                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30894                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30895
30896                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30897                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30898                 
30899                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30900                 
30901                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30902                 
30903                 break;
30904             case 180 :
30905                 
30906                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30907                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30908                 
30909                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30910                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30911                 
30912                 var targetWidth = this.minWidth - 2 * x;
30913                 var targetHeight = this.minHeight - 2 * y;
30914                 
30915                 var scale = 1;
30916                 
30917                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30918                     scale = targetWidth / width;
30919                 }
30920                 
30921                 if(x > 0 && y == 0){
30922                     scale = targetHeight / height;
30923                 }
30924                 
30925                 if(x > 0 && y > 0){
30926                     scale = targetWidth / width;
30927                     
30928                     if(width < height){
30929                         scale = targetHeight / height;
30930                     }
30931                 }
30932                 
30933                 context.scale(scale, scale);
30934                 
30935                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30936                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30937
30938                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30939                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30940
30941                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30942                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30943                 
30944                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30945                 
30946                 break;
30947             case 270 :
30948                 
30949                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30950                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30951                 
30952                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30953                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30954                 
30955                 var targetWidth = this.minWidth - 2 * x;
30956                 var targetHeight = this.minHeight - 2 * y;
30957                 
30958                 var scale = 1;
30959                 
30960                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30961                     scale = targetWidth / width;
30962                 }
30963                 
30964                 if(x > 0 && y == 0){
30965                     scale = targetHeight / height;
30966                 }
30967                 
30968                 if(x > 0 && y > 0){
30969                     scale = targetWidth / width;
30970                     
30971                     if(width < height){
30972                         scale = targetHeight / height;
30973                     }
30974                 }
30975                 
30976                 context.scale(scale, scale);
30977                 
30978                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30979                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30980
30981                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30982                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30983                 
30984                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30985                 
30986                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30987                 
30988                 break;
30989             default : 
30990                 break;
30991         }
30992         
30993         this.cropData = canvas.toDataURL(this.cropType);
30994         
30995         if(this.fireEvent('crop', this, this.cropData) !== false){
30996             this.process(this.file, this.cropData);
30997         }
30998         
30999         return;
31000         
31001     },
31002     
31003     setThumbBoxSize : function()
31004     {
31005         var width, height;
31006         
31007         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31008             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31009             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31010             
31011             this.minWidth = width;
31012             this.minHeight = height;
31013             
31014             if(this.rotate == 90 || this.rotate == 270){
31015                 this.minWidth = height;
31016                 this.minHeight = width;
31017             }
31018         }
31019         
31020         height = 300;
31021         width = Math.ceil(this.minWidth * height / this.minHeight);
31022         
31023         if(this.minWidth > this.minHeight){
31024             width = 300;
31025             height = Math.ceil(this.minHeight * width / this.minWidth);
31026         }
31027         
31028         this.thumbEl.setStyle({
31029             width : width + 'px',
31030             height : height + 'px'
31031         });
31032
31033         return;
31034             
31035     },
31036     
31037     setThumbBoxPosition : function()
31038     {
31039         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31040         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31041         
31042         this.thumbEl.setLeft(x);
31043         this.thumbEl.setTop(y);
31044         
31045     },
31046     
31047     baseRotateLevel : function()
31048     {
31049         this.baseRotate = 1;
31050         
31051         if(
31052                 typeof(this.exif) != 'undefined' &&
31053                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31054                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31055         ){
31056             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31057         }
31058         
31059         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31060         
31061     },
31062     
31063     baseScaleLevel : function()
31064     {
31065         var width, height;
31066         
31067         if(this.isDocument){
31068             
31069             if(this.baseRotate == 6 || this.baseRotate == 8){
31070             
31071                 height = this.thumbEl.getHeight();
31072                 this.baseScale = height / this.imageEl.OriginWidth;
31073
31074                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31075                     width = this.thumbEl.getWidth();
31076                     this.baseScale = width / this.imageEl.OriginHeight;
31077                 }
31078
31079                 return;
31080             }
31081
31082             height = this.thumbEl.getHeight();
31083             this.baseScale = height / this.imageEl.OriginHeight;
31084
31085             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31086                 width = this.thumbEl.getWidth();
31087                 this.baseScale = width / this.imageEl.OriginWidth;
31088             }
31089
31090             return;
31091         }
31092         
31093         if(this.baseRotate == 6 || this.baseRotate == 8){
31094             
31095             width = this.thumbEl.getHeight();
31096             this.baseScale = width / this.imageEl.OriginHeight;
31097             
31098             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31099                 height = this.thumbEl.getWidth();
31100                 this.baseScale = height / this.imageEl.OriginHeight;
31101             }
31102             
31103             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31104                 height = this.thumbEl.getWidth();
31105                 this.baseScale = height / this.imageEl.OriginHeight;
31106                 
31107                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31108                     width = this.thumbEl.getHeight();
31109                     this.baseScale = width / this.imageEl.OriginWidth;
31110                 }
31111             }
31112             
31113             return;
31114         }
31115         
31116         width = this.thumbEl.getWidth();
31117         this.baseScale = width / this.imageEl.OriginWidth;
31118         
31119         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31120             height = this.thumbEl.getHeight();
31121             this.baseScale = height / this.imageEl.OriginHeight;
31122         }
31123         
31124         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31125             
31126             height = this.thumbEl.getHeight();
31127             this.baseScale = height / this.imageEl.OriginHeight;
31128             
31129             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31130                 width = this.thumbEl.getWidth();
31131                 this.baseScale = width / this.imageEl.OriginWidth;
31132             }
31133             
31134         }
31135         
31136         return;
31137     },
31138     
31139     getScaleLevel : function()
31140     {
31141         return this.baseScale * Math.pow(1.1, this.scale);
31142     },
31143     
31144     onTouchStart : function(e)
31145     {
31146         if(!this.canvasLoaded){
31147             this.beforeSelectFile(e);
31148             return;
31149         }
31150         
31151         var touches = e.browserEvent.touches;
31152         
31153         if(!touches){
31154             return;
31155         }
31156         
31157         if(touches.length == 1){
31158             this.onMouseDown(e);
31159             return;
31160         }
31161         
31162         if(touches.length != 2){
31163             return;
31164         }
31165         
31166         var coords = [];
31167         
31168         for(var i = 0, finger; finger = touches[i]; i++){
31169             coords.push(finger.pageX, finger.pageY);
31170         }
31171         
31172         var x = Math.pow(coords[0] - coords[2], 2);
31173         var y = Math.pow(coords[1] - coords[3], 2);
31174         
31175         this.startDistance = Math.sqrt(x + y);
31176         
31177         this.startScale = this.scale;
31178         
31179         this.pinching = true;
31180         this.dragable = false;
31181         
31182     },
31183     
31184     onTouchMove : function(e)
31185     {
31186         if(!this.pinching && !this.dragable){
31187             return;
31188         }
31189         
31190         var touches = e.browserEvent.touches;
31191         
31192         if(!touches){
31193             return;
31194         }
31195         
31196         if(this.dragable){
31197             this.onMouseMove(e);
31198             return;
31199         }
31200         
31201         var coords = [];
31202         
31203         for(var i = 0, finger; finger = touches[i]; i++){
31204             coords.push(finger.pageX, finger.pageY);
31205         }
31206         
31207         var x = Math.pow(coords[0] - coords[2], 2);
31208         var y = Math.pow(coords[1] - coords[3], 2);
31209         
31210         this.endDistance = Math.sqrt(x + y);
31211         
31212         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31213         
31214         if(!this.zoomable()){
31215             this.scale = this.startScale;
31216             return;
31217         }
31218         
31219         this.draw();
31220         
31221     },
31222     
31223     onTouchEnd : function(e)
31224     {
31225         this.pinching = false;
31226         this.dragable = false;
31227         
31228     },
31229     
31230     process : function(file, crop)
31231     {
31232         if(this.loadMask){
31233             this.maskEl.mask(this.loadingText);
31234         }
31235         
31236         this.xhr = new XMLHttpRequest();
31237         
31238         file.xhr = this.xhr;
31239
31240         this.xhr.open(this.method, this.url, true);
31241         
31242         var headers = {
31243             "Accept": "application/json",
31244             "Cache-Control": "no-cache",
31245             "X-Requested-With": "XMLHttpRequest"
31246         };
31247         
31248         for (var headerName in headers) {
31249             var headerValue = headers[headerName];
31250             if (headerValue) {
31251                 this.xhr.setRequestHeader(headerName, headerValue);
31252             }
31253         }
31254         
31255         var _this = this;
31256         
31257         this.xhr.onload = function()
31258         {
31259             _this.xhrOnLoad(_this.xhr);
31260         }
31261         
31262         this.xhr.onerror = function()
31263         {
31264             _this.xhrOnError(_this.xhr);
31265         }
31266         
31267         var formData = new FormData();
31268
31269         formData.append('returnHTML', 'NO');
31270         
31271         if(crop){
31272             formData.append('crop', crop);
31273         }
31274         
31275         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31276             formData.append(this.paramName, file, file.name);
31277         }
31278         
31279         if(typeof(file.filename) != 'undefined'){
31280             formData.append('filename', file.filename);
31281         }
31282         
31283         if(typeof(file.mimetype) != 'undefined'){
31284             formData.append('mimetype', file.mimetype);
31285         }
31286         
31287         if(this.fireEvent('arrange', this, formData) != false){
31288             this.xhr.send(formData);
31289         };
31290     },
31291     
31292     xhrOnLoad : function(xhr)
31293     {
31294         if(this.loadMask){
31295             this.maskEl.unmask();
31296         }
31297         
31298         if (xhr.readyState !== 4) {
31299             this.fireEvent('exception', this, xhr);
31300             return;
31301         }
31302
31303         var response = Roo.decode(xhr.responseText);
31304         
31305         if(!response.success){
31306             this.fireEvent('exception', this, xhr);
31307             return;
31308         }
31309         
31310         var response = Roo.decode(xhr.responseText);
31311         
31312         this.fireEvent('upload', this, response);
31313         
31314     },
31315     
31316     xhrOnError : function()
31317     {
31318         if(this.loadMask){
31319             this.maskEl.unmask();
31320         }
31321         
31322         Roo.log('xhr on error');
31323         
31324         var response = Roo.decode(xhr.responseText);
31325           
31326         Roo.log(response);
31327         
31328     },
31329     
31330     prepare : function(file)
31331     {   
31332         if(this.loadMask){
31333             this.maskEl.mask(this.loadingText);
31334         }
31335         
31336         this.file = false;
31337         this.exif = {};
31338         
31339         if(typeof(file) === 'string'){
31340             this.loadCanvas(file);
31341             return;
31342         }
31343         
31344         if(!file || !this.urlAPI){
31345             return;
31346         }
31347         
31348         this.file = file;
31349         this.cropType = file.type;
31350         
31351         var _this = this;
31352         
31353         if(this.fireEvent('prepare', this, this.file) != false){
31354             
31355             var reader = new FileReader();
31356             
31357             reader.onload = function (e) {
31358                 if (e.target.error) {
31359                     Roo.log(e.target.error);
31360                     return;
31361                 }
31362                 
31363                 var buffer = e.target.result,
31364                     dataView = new DataView(buffer),
31365                     offset = 2,
31366                     maxOffset = dataView.byteLength - 4,
31367                     markerBytes,
31368                     markerLength;
31369                 
31370                 if (dataView.getUint16(0) === 0xffd8) {
31371                     while (offset < maxOffset) {
31372                         markerBytes = dataView.getUint16(offset);
31373                         
31374                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31375                             markerLength = dataView.getUint16(offset + 2) + 2;
31376                             if (offset + markerLength > dataView.byteLength) {
31377                                 Roo.log('Invalid meta data: Invalid segment size.');
31378                                 break;
31379                             }
31380                             
31381                             if(markerBytes == 0xffe1){
31382                                 _this.parseExifData(
31383                                     dataView,
31384                                     offset,
31385                                     markerLength
31386                                 );
31387                             }
31388                             
31389                             offset += markerLength;
31390                             
31391                             continue;
31392                         }
31393                         
31394                         break;
31395                     }
31396                     
31397                 }
31398                 
31399                 var url = _this.urlAPI.createObjectURL(_this.file);
31400                 
31401                 _this.loadCanvas(url);
31402                 
31403                 return;
31404             }
31405             
31406             reader.readAsArrayBuffer(this.file);
31407             
31408         }
31409         
31410     },
31411     
31412     parseExifData : function(dataView, offset, length)
31413     {
31414         var tiffOffset = offset + 10,
31415             littleEndian,
31416             dirOffset;
31417     
31418         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31419             // No Exif data, might be XMP data instead
31420             return;
31421         }
31422         
31423         // Check for the ASCII code for "Exif" (0x45786966):
31424         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31425             // No Exif data, might be XMP data instead
31426             return;
31427         }
31428         if (tiffOffset + 8 > dataView.byteLength) {
31429             Roo.log('Invalid Exif data: Invalid segment size.');
31430             return;
31431         }
31432         // Check for the two null bytes:
31433         if (dataView.getUint16(offset + 8) !== 0x0000) {
31434             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31435             return;
31436         }
31437         // Check the byte alignment:
31438         switch (dataView.getUint16(tiffOffset)) {
31439         case 0x4949:
31440             littleEndian = true;
31441             break;
31442         case 0x4D4D:
31443             littleEndian = false;
31444             break;
31445         default:
31446             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31447             return;
31448         }
31449         // Check for the TIFF tag marker (0x002A):
31450         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31451             Roo.log('Invalid Exif data: Missing TIFF marker.');
31452             return;
31453         }
31454         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31455         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31456         
31457         this.parseExifTags(
31458             dataView,
31459             tiffOffset,
31460             tiffOffset + dirOffset,
31461             littleEndian
31462         );
31463     },
31464     
31465     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31466     {
31467         var tagsNumber,
31468             dirEndOffset,
31469             i;
31470         if (dirOffset + 6 > dataView.byteLength) {
31471             Roo.log('Invalid Exif data: Invalid directory offset.');
31472             return;
31473         }
31474         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31475         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31476         if (dirEndOffset + 4 > dataView.byteLength) {
31477             Roo.log('Invalid Exif data: Invalid directory size.');
31478             return;
31479         }
31480         for (i = 0; i < tagsNumber; i += 1) {
31481             this.parseExifTag(
31482                 dataView,
31483                 tiffOffset,
31484                 dirOffset + 2 + 12 * i, // tag offset
31485                 littleEndian
31486             );
31487         }
31488         // Return the offset to the next directory:
31489         return dataView.getUint32(dirEndOffset, littleEndian);
31490     },
31491     
31492     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31493     {
31494         var tag = dataView.getUint16(offset, littleEndian);
31495         
31496         this.exif[tag] = this.getExifValue(
31497             dataView,
31498             tiffOffset,
31499             offset,
31500             dataView.getUint16(offset + 2, littleEndian), // tag type
31501             dataView.getUint32(offset + 4, littleEndian), // tag length
31502             littleEndian
31503         );
31504     },
31505     
31506     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31507     {
31508         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31509             tagSize,
31510             dataOffset,
31511             values,
31512             i,
31513             str,
31514             c;
31515     
31516         if (!tagType) {
31517             Roo.log('Invalid Exif data: Invalid tag type.');
31518             return;
31519         }
31520         
31521         tagSize = tagType.size * length;
31522         // Determine if the value is contained in the dataOffset bytes,
31523         // or if the value at the dataOffset is a pointer to the actual data:
31524         dataOffset = tagSize > 4 ?
31525                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31526         if (dataOffset + tagSize > dataView.byteLength) {
31527             Roo.log('Invalid Exif data: Invalid data offset.');
31528             return;
31529         }
31530         if (length === 1) {
31531             return tagType.getValue(dataView, dataOffset, littleEndian);
31532         }
31533         values = [];
31534         for (i = 0; i < length; i += 1) {
31535             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31536         }
31537         
31538         if (tagType.ascii) {
31539             str = '';
31540             // Concatenate the chars:
31541             for (i = 0; i < values.length; i += 1) {
31542                 c = values[i];
31543                 // Ignore the terminating NULL byte(s):
31544                 if (c === '\u0000') {
31545                     break;
31546                 }
31547                 str += c;
31548             }
31549             return str;
31550         }
31551         return values;
31552     }
31553     
31554 });
31555
31556 Roo.apply(Roo.bootstrap.UploadCropbox, {
31557     tags : {
31558         'Orientation': 0x0112
31559     },
31560     
31561     Orientation: {
31562             1: 0, //'top-left',
31563 //            2: 'top-right',
31564             3: 180, //'bottom-right',
31565 //            4: 'bottom-left',
31566 //            5: 'left-top',
31567             6: 90, //'right-top',
31568 //            7: 'right-bottom',
31569             8: 270 //'left-bottom'
31570     },
31571     
31572     exifTagTypes : {
31573         // byte, 8-bit unsigned int:
31574         1: {
31575             getValue: function (dataView, dataOffset) {
31576                 return dataView.getUint8(dataOffset);
31577             },
31578             size: 1
31579         },
31580         // ascii, 8-bit byte:
31581         2: {
31582             getValue: function (dataView, dataOffset) {
31583                 return String.fromCharCode(dataView.getUint8(dataOffset));
31584             },
31585             size: 1,
31586             ascii: true
31587         },
31588         // short, 16 bit int:
31589         3: {
31590             getValue: function (dataView, dataOffset, littleEndian) {
31591                 return dataView.getUint16(dataOffset, littleEndian);
31592             },
31593             size: 2
31594         },
31595         // long, 32 bit int:
31596         4: {
31597             getValue: function (dataView, dataOffset, littleEndian) {
31598                 return dataView.getUint32(dataOffset, littleEndian);
31599             },
31600             size: 4
31601         },
31602         // rational = two long values, first is numerator, second is denominator:
31603         5: {
31604             getValue: function (dataView, dataOffset, littleEndian) {
31605                 return dataView.getUint32(dataOffset, littleEndian) /
31606                     dataView.getUint32(dataOffset + 4, littleEndian);
31607             },
31608             size: 8
31609         },
31610         // slong, 32 bit signed int:
31611         9: {
31612             getValue: function (dataView, dataOffset, littleEndian) {
31613                 return dataView.getInt32(dataOffset, littleEndian);
31614             },
31615             size: 4
31616         },
31617         // srational, two slongs, first is numerator, second is denominator:
31618         10: {
31619             getValue: function (dataView, dataOffset, littleEndian) {
31620                 return dataView.getInt32(dataOffset, littleEndian) /
31621                     dataView.getInt32(dataOffset + 4, littleEndian);
31622             },
31623             size: 8
31624         }
31625     },
31626     
31627     footer : {
31628         STANDARD : [
31629             {
31630                 tag : 'div',
31631                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31632                 action : 'rotate-left',
31633                 cn : [
31634                     {
31635                         tag : 'button',
31636                         cls : 'btn btn-default',
31637                         html : '<i class="fa fa-undo"></i>'
31638                     }
31639                 ]
31640             },
31641             {
31642                 tag : 'div',
31643                 cls : 'btn-group roo-upload-cropbox-picture',
31644                 action : 'picture',
31645                 cn : [
31646                     {
31647                         tag : 'button',
31648                         cls : 'btn btn-default',
31649                         html : '<i class="fa fa-picture-o"></i>'
31650                     }
31651                 ]
31652             },
31653             {
31654                 tag : 'div',
31655                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31656                 action : 'rotate-right',
31657                 cn : [
31658                     {
31659                         tag : 'button',
31660                         cls : 'btn btn-default',
31661                         html : '<i class="fa fa-repeat"></i>'
31662                     }
31663                 ]
31664             }
31665         ],
31666         DOCUMENT : [
31667             {
31668                 tag : 'div',
31669                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31670                 action : 'rotate-left',
31671                 cn : [
31672                     {
31673                         tag : 'button',
31674                         cls : 'btn btn-default',
31675                         html : '<i class="fa fa-undo"></i>'
31676                     }
31677                 ]
31678             },
31679             {
31680                 tag : 'div',
31681                 cls : 'btn-group roo-upload-cropbox-download',
31682                 action : 'download',
31683                 cn : [
31684                     {
31685                         tag : 'button',
31686                         cls : 'btn btn-default',
31687                         html : '<i class="fa fa-download"></i>'
31688                     }
31689                 ]
31690             },
31691             {
31692                 tag : 'div',
31693                 cls : 'btn-group roo-upload-cropbox-crop',
31694                 action : 'crop',
31695                 cn : [
31696                     {
31697                         tag : 'button',
31698                         cls : 'btn btn-default',
31699                         html : '<i class="fa fa-crop"></i>'
31700                     }
31701                 ]
31702             },
31703             {
31704                 tag : 'div',
31705                 cls : 'btn-group roo-upload-cropbox-trash',
31706                 action : 'trash',
31707                 cn : [
31708                     {
31709                         tag : 'button',
31710                         cls : 'btn btn-default',
31711                         html : '<i class="fa fa-trash"></i>'
31712                     }
31713                 ]
31714             },
31715             {
31716                 tag : 'div',
31717                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31718                 action : 'rotate-right',
31719                 cn : [
31720                     {
31721                         tag : 'button',
31722                         cls : 'btn btn-default',
31723                         html : '<i class="fa fa-repeat"></i>'
31724                     }
31725                 ]
31726             }
31727         ],
31728         ROTATOR : [
31729             {
31730                 tag : 'div',
31731                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31732                 action : 'rotate-left',
31733                 cn : [
31734                     {
31735                         tag : 'button',
31736                         cls : 'btn btn-default',
31737                         html : '<i class="fa fa-undo"></i>'
31738                     }
31739                 ]
31740             },
31741             {
31742                 tag : 'div',
31743                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31744                 action : 'rotate-right',
31745                 cn : [
31746                     {
31747                         tag : 'button',
31748                         cls : 'btn btn-default',
31749                         html : '<i class="fa fa-repeat"></i>'
31750                     }
31751                 ]
31752             }
31753         ]
31754     }
31755 });
31756
31757 /*
31758 * Licence: LGPL
31759 */
31760
31761 /**
31762  * @class Roo.bootstrap.DocumentManager
31763  * @extends Roo.bootstrap.Component
31764  * Bootstrap DocumentManager class
31765  * @cfg {String} paramName default 'imageUpload'
31766  * @cfg {String} toolTipName default 'filename'
31767  * @cfg {String} method default POST
31768  * @cfg {String} url action url
31769  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31770  * @cfg {Boolean} multiple multiple upload default true
31771  * @cfg {Number} thumbSize default 300
31772  * @cfg {String} fieldLabel
31773  * @cfg {Number} labelWidth default 4
31774  * @cfg {String} labelAlign (left|top) default left
31775  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31776 * @cfg {Number} labellg set the width of label (1-12)
31777  * @cfg {Number} labelmd set the width of label (1-12)
31778  * @cfg {Number} labelsm set the width of label (1-12)
31779  * @cfg {Number} labelxs set the width of label (1-12)
31780  * 
31781  * @constructor
31782  * Create a new DocumentManager
31783  * @param {Object} config The config object
31784  */
31785
31786 Roo.bootstrap.DocumentManager = function(config){
31787     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31788     
31789     this.files = [];
31790     this.delegates = [];
31791     
31792     this.addEvents({
31793         /**
31794          * @event initial
31795          * Fire when initial the DocumentManager
31796          * @param {Roo.bootstrap.DocumentManager} this
31797          */
31798         "initial" : true,
31799         /**
31800          * @event inspect
31801          * inspect selected file
31802          * @param {Roo.bootstrap.DocumentManager} this
31803          * @param {File} file
31804          */
31805         "inspect" : true,
31806         /**
31807          * @event exception
31808          * Fire when xhr load exception
31809          * @param {Roo.bootstrap.DocumentManager} this
31810          * @param {XMLHttpRequest} xhr
31811          */
31812         "exception" : true,
31813         /**
31814          * @event afterupload
31815          * Fire when xhr load exception
31816          * @param {Roo.bootstrap.DocumentManager} this
31817          * @param {XMLHttpRequest} xhr
31818          */
31819         "afterupload" : true,
31820         /**
31821          * @event prepare
31822          * prepare the form data
31823          * @param {Roo.bootstrap.DocumentManager} this
31824          * @param {Object} formData
31825          */
31826         "prepare" : true,
31827         /**
31828          * @event remove
31829          * Fire when remove the file
31830          * @param {Roo.bootstrap.DocumentManager} this
31831          * @param {Object} file
31832          */
31833         "remove" : true,
31834         /**
31835          * @event refresh
31836          * Fire after refresh the file
31837          * @param {Roo.bootstrap.DocumentManager} this
31838          */
31839         "refresh" : true,
31840         /**
31841          * @event click
31842          * Fire after click the image
31843          * @param {Roo.bootstrap.DocumentManager} this
31844          * @param {Object} file
31845          */
31846         "click" : true,
31847         /**
31848          * @event edit
31849          * Fire when upload a image and editable set to true
31850          * @param {Roo.bootstrap.DocumentManager} this
31851          * @param {Object} file
31852          */
31853         "edit" : true,
31854         /**
31855          * @event beforeselectfile
31856          * Fire before select file
31857          * @param {Roo.bootstrap.DocumentManager} this
31858          */
31859         "beforeselectfile" : true,
31860         /**
31861          * @event process
31862          * Fire before process file
31863          * @param {Roo.bootstrap.DocumentManager} this
31864          * @param {Object} file
31865          */
31866         "process" : true,
31867         /**
31868          * @event previewrendered
31869          * Fire when preview rendered
31870          * @param {Roo.bootstrap.DocumentManager} this
31871          * @param {Object} file
31872          */
31873         "previewrendered" : true,
31874         /**
31875          */
31876         "previewResize" : true
31877         
31878     });
31879 };
31880
31881 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31882     
31883     boxes : 0,
31884     inputName : '',
31885     thumbSize : 300,
31886     multiple : true,
31887     files : false,
31888     method : 'POST',
31889     url : '',
31890     paramName : 'imageUpload',
31891     toolTipName : 'filename',
31892     fieldLabel : '',
31893     labelWidth : 4,
31894     labelAlign : 'left',
31895     editable : true,
31896     delegates : false,
31897     xhr : false, 
31898     
31899     labellg : 0,
31900     labelmd : 0,
31901     labelsm : 0,
31902     labelxs : 0,
31903     
31904     getAutoCreate : function()
31905     {   
31906         var managerWidget = {
31907             tag : 'div',
31908             cls : 'roo-document-manager',
31909             cn : [
31910                 {
31911                     tag : 'input',
31912                     cls : 'roo-document-manager-selector',
31913                     type : 'file'
31914                 },
31915                 {
31916                     tag : 'div',
31917                     cls : 'roo-document-manager-uploader',
31918                     cn : [
31919                         {
31920                             tag : 'div',
31921                             cls : 'roo-document-manager-upload-btn',
31922                             html : '<i class="fa fa-plus"></i>'
31923                         }
31924                     ]
31925                     
31926                 }
31927             ]
31928         };
31929         
31930         var content = [
31931             {
31932                 tag : 'div',
31933                 cls : 'column col-md-12',
31934                 cn : managerWidget
31935             }
31936         ];
31937         
31938         if(this.fieldLabel.length){
31939             
31940             content = [
31941                 {
31942                     tag : 'div',
31943                     cls : 'column col-md-12',
31944                     html : this.fieldLabel
31945                 },
31946                 {
31947                     tag : 'div',
31948                     cls : 'column col-md-12',
31949                     cn : managerWidget
31950                 }
31951             ];
31952
31953             if(this.labelAlign == 'left'){
31954                 content = [
31955                     {
31956                         tag : 'div',
31957                         cls : 'column',
31958                         html : this.fieldLabel
31959                     },
31960                     {
31961                         tag : 'div',
31962                         cls : 'column',
31963                         cn : managerWidget
31964                     }
31965                 ];
31966                 
31967                 if(this.labelWidth > 12){
31968                     content[0].style = "width: " + this.labelWidth + 'px';
31969                 }
31970
31971                 if(this.labelWidth < 13 && this.labelmd == 0){
31972                     this.labelmd = this.labelWidth;
31973                 }
31974
31975                 if(this.labellg > 0){
31976                     content[0].cls += ' col-lg-' + this.labellg;
31977                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31978                 }
31979
31980                 if(this.labelmd > 0){
31981                     content[0].cls += ' col-md-' + this.labelmd;
31982                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31983                 }
31984
31985                 if(this.labelsm > 0){
31986                     content[0].cls += ' col-sm-' + this.labelsm;
31987                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31988                 }
31989
31990                 if(this.labelxs > 0){
31991                     content[0].cls += ' col-xs-' + this.labelxs;
31992                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31993                 }
31994                 
31995             }
31996         }
31997         
31998         var cfg = {
31999             tag : 'div',
32000             cls : 'row clearfix',
32001             cn : content
32002         };
32003         
32004         return cfg;
32005         
32006     },
32007     
32008     initEvents : function()
32009     {
32010         this.managerEl = this.el.select('.roo-document-manager', true).first();
32011         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32012         
32013         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32014         this.selectorEl.hide();
32015         
32016         if(this.multiple){
32017             this.selectorEl.attr('multiple', 'multiple');
32018         }
32019         
32020         this.selectorEl.on('change', this.onFileSelected, this);
32021         
32022         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32023         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32024         
32025         this.uploader.on('click', this.onUploaderClick, this);
32026         
32027         this.renderProgressDialog();
32028         
32029         var _this = this;
32030         
32031         window.addEventListener("resize", function() { _this.refresh(); } );
32032         
32033         this.fireEvent('initial', this);
32034     },
32035     
32036     renderProgressDialog : function()
32037     {
32038         var _this = this;
32039         
32040         this.progressDialog = new Roo.bootstrap.Modal({
32041             cls : 'roo-document-manager-progress-dialog',
32042             allow_close : false,
32043             animate : false,
32044             title : '',
32045             buttons : [
32046                 {
32047                     name  :'cancel',
32048                     weight : 'danger',
32049                     html : 'Cancel'
32050                 }
32051             ], 
32052             listeners : { 
32053                 btnclick : function() {
32054                     _this.uploadCancel();
32055                     this.hide();
32056                 }
32057             }
32058         });
32059          
32060         this.progressDialog.render(Roo.get(document.body));
32061          
32062         this.progress = new Roo.bootstrap.Progress({
32063             cls : 'roo-document-manager-progress',
32064             active : true,
32065             striped : true
32066         });
32067         
32068         this.progress.render(this.progressDialog.getChildContainer());
32069         
32070         this.progressBar = new Roo.bootstrap.ProgressBar({
32071             cls : 'roo-document-manager-progress-bar',
32072             aria_valuenow : 0,
32073             aria_valuemin : 0,
32074             aria_valuemax : 12,
32075             panel : 'success'
32076         });
32077         
32078         this.progressBar.render(this.progress.getChildContainer());
32079     },
32080     
32081     onUploaderClick : function(e)
32082     {
32083         e.preventDefault();
32084      
32085         if(this.fireEvent('beforeselectfile', this) != false){
32086             this.selectorEl.dom.click();
32087         }
32088         
32089     },
32090     
32091     onFileSelected : function(e)
32092     {
32093         e.preventDefault();
32094         
32095         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32096             return;
32097         }
32098         
32099         Roo.each(this.selectorEl.dom.files, function(file){
32100             if(this.fireEvent('inspect', this, file) != false){
32101                 this.files.push(file);
32102             }
32103         }, this);
32104         
32105         this.queue();
32106         
32107     },
32108     
32109     queue : function()
32110     {
32111         this.selectorEl.dom.value = '';
32112         
32113         if(!this.files || !this.files.length){
32114             return;
32115         }
32116         
32117         if(this.boxes > 0 && this.files.length > this.boxes){
32118             this.files = this.files.slice(0, this.boxes);
32119         }
32120         
32121         this.uploader.show();
32122         
32123         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32124             this.uploader.hide();
32125         }
32126         
32127         var _this = this;
32128         
32129         var files = [];
32130         
32131         var docs = [];
32132         
32133         Roo.each(this.files, function(file){
32134             
32135             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32136                 var f = this.renderPreview(file);
32137                 files.push(f);
32138                 return;
32139             }
32140             
32141             if(file.type.indexOf('image') != -1){
32142                 this.delegates.push(
32143                     (function(){
32144                         _this.process(file);
32145                     }).createDelegate(this)
32146                 );
32147         
32148                 return;
32149             }
32150             
32151             docs.push(
32152                 (function(){
32153                     _this.process(file);
32154                 }).createDelegate(this)
32155             );
32156             
32157         }, this);
32158         
32159         this.files = files;
32160         
32161         this.delegates = this.delegates.concat(docs);
32162         
32163         if(!this.delegates.length){
32164             this.refresh();
32165             return;
32166         }
32167         
32168         this.progressBar.aria_valuemax = this.delegates.length;
32169         
32170         this.arrange();
32171         
32172         return;
32173     },
32174     
32175     arrange : function()
32176     {
32177         if(!this.delegates.length){
32178             this.progressDialog.hide();
32179             this.refresh();
32180             return;
32181         }
32182         
32183         var delegate = this.delegates.shift();
32184         
32185         this.progressDialog.show();
32186         
32187         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32188         
32189         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32190         
32191         delegate();
32192     },
32193     
32194     refresh : function()
32195     {
32196         this.uploader.show();
32197         
32198         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32199             this.uploader.hide();
32200         }
32201         
32202         Roo.isTouch ? this.closable(false) : this.closable(true);
32203         
32204         this.fireEvent('refresh', this);
32205     },
32206     
32207     onRemove : function(e, el, o)
32208     {
32209         e.preventDefault();
32210         
32211         this.fireEvent('remove', this, o);
32212         
32213     },
32214     
32215     remove : function(o)
32216     {
32217         var files = [];
32218         
32219         Roo.each(this.files, function(file){
32220             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32221                 files.push(file);
32222                 return;
32223             }
32224
32225             o.target.remove();
32226
32227         }, this);
32228         
32229         this.files = files;
32230         
32231         this.refresh();
32232     },
32233     
32234     clear : function()
32235     {
32236         Roo.each(this.files, function(file){
32237             if(!file.target){
32238                 return;
32239             }
32240             
32241             file.target.remove();
32242
32243         }, this);
32244         
32245         this.files = [];
32246         
32247         this.refresh();
32248     },
32249     
32250     onClick : function(e, el, o)
32251     {
32252         e.preventDefault();
32253         
32254         this.fireEvent('click', this, o);
32255         
32256     },
32257     
32258     closable : function(closable)
32259     {
32260         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32261             
32262             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32263             
32264             if(closable){
32265                 el.show();
32266                 return;
32267             }
32268             
32269             el.hide();
32270             
32271         }, this);
32272     },
32273     
32274     xhrOnLoad : function(xhr)
32275     {
32276         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32277             el.remove();
32278         }, this);
32279         
32280         if (xhr.readyState !== 4) {
32281             this.arrange();
32282             this.fireEvent('exception', this, xhr);
32283             return;
32284         }
32285
32286         var response = Roo.decode(xhr.responseText);
32287         
32288         if(!response.success){
32289             this.arrange();
32290             this.fireEvent('exception', this, xhr);
32291             return;
32292         }
32293         
32294         var file = this.renderPreview(response.data);
32295         
32296         this.files.push(file);
32297         
32298         this.arrange();
32299         
32300         this.fireEvent('afterupload', this, xhr);
32301         
32302     },
32303     
32304     xhrOnError : function(xhr)
32305     {
32306         Roo.log('xhr on error');
32307         
32308         var response = Roo.decode(xhr.responseText);
32309           
32310         Roo.log(response);
32311         
32312         this.arrange();
32313     },
32314     
32315     process : function(file)
32316     {
32317         if(this.fireEvent('process', this, file) !== false){
32318             if(this.editable && file.type.indexOf('image') != -1){
32319                 this.fireEvent('edit', this, file);
32320                 return;
32321             }
32322
32323             this.uploadStart(file, false);
32324
32325             return;
32326         }
32327         
32328     },
32329     
32330     uploadStart : function(file, crop)
32331     {
32332         this.xhr = new XMLHttpRequest();
32333         
32334         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32335             this.arrange();
32336             return;
32337         }
32338         
32339         file.xhr = this.xhr;
32340             
32341         this.managerEl.createChild({
32342             tag : 'div',
32343             cls : 'roo-document-manager-loading',
32344             cn : [
32345                 {
32346                     tag : 'div',
32347                     tooltip : file.name,
32348                     cls : 'roo-document-manager-thumb',
32349                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32350                 }
32351             ]
32352
32353         });
32354
32355         this.xhr.open(this.method, this.url, true);
32356         
32357         var headers = {
32358             "Accept": "application/json",
32359             "Cache-Control": "no-cache",
32360             "X-Requested-With": "XMLHttpRequest"
32361         };
32362         
32363         for (var headerName in headers) {
32364             var headerValue = headers[headerName];
32365             if (headerValue) {
32366                 this.xhr.setRequestHeader(headerName, headerValue);
32367             }
32368         }
32369         
32370         var _this = this;
32371         
32372         this.xhr.onload = function()
32373         {
32374             _this.xhrOnLoad(_this.xhr);
32375         }
32376         
32377         this.xhr.onerror = function()
32378         {
32379             _this.xhrOnError(_this.xhr);
32380         }
32381         
32382         var formData = new FormData();
32383
32384         formData.append('returnHTML', 'NO');
32385         
32386         if(crop){
32387             formData.append('crop', crop);
32388         }
32389         
32390         formData.append(this.paramName, file, file.name);
32391         
32392         var options = {
32393             file : file, 
32394             manually : false
32395         };
32396         
32397         if(this.fireEvent('prepare', this, formData, options) != false){
32398             
32399             if(options.manually){
32400                 return;
32401             }
32402             
32403             this.xhr.send(formData);
32404             return;
32405         };
32406         
32407         this.uploadCancel();
32408     },
32409     
32410     uploadCancel : function()
32411     {
32412         if (this.xhr) {
32413             this.xhr.abort();
32414         }
32415         
32416         this.delegates = [];
32417         
32418         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32419             el.remove();
32420         }, this);
32421         
32422         this.arrange();
32423     },
32424     
32425     renderPreview : function(file)
32426     {
32427         if(typeof(file.target) != 'undefined' && file.target){
32428             return file;
32429         }
32430         
32431         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32432         
32433         var previewEl = this.managerEl.createChild({
32434             tag : 'div',
32435             cls : 'roo-document-manager-preview',
32436             cn : [
32437                 {
32438                     tag : 'div',
32439                     tooltip : file[this.toolTipName],
32440                     cls : 'roo-document-manager-thumb',
32441                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32442                 },
32443                 {
32444                     tag : 'button',
32445                     cls : 'close',
32446                     html : '<i class="fa fa-times-circle"></i>'
32447                 }
32448             ]
32449         });
32450
32451         var close = previewEl.select('button.close', true).first();
32452
32453         close.on('click', this.onRemove, this, file);
32454
32455         file.target = previewEl;
32456
32457         var image = previewEl.select('img', true).first();
32458         
32459         var _this = this;
32460         
32461         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32462         
32463         image.on('click', this.onClick, this, file);
32464         
32465         this.fireEvent('previewrendered', this, file);
32466         
32467         return file;
32468         
32469     },
32470     
32471     onPreviewLoad : function(file, image)
32472     {
32473         if(typeof(file.target) == 'undefined' || !file.target){
32474             return;
32475         }
32476         
32477         var width = image.dom.naturalWidth || image.dom.width;
32478         var height = image.dom.naturalHeight || image.dom.height;
32479         
32480         if(!this.previewResize) {
32481             return;
32482         }
32483         
32484         if(width > height){
32485             file.target.addClass('wide');
32486             return;
32487         }
32488         
32489         file.target.addClass('tall');
32490         return;
32491         
32492     },
32493     
32494     uploadFromSource : function(file, crop)
32495     {
32496         this.xhr = new XMLHttpRequest();
32497         
32498         this.managerEl.createChild({
32499             tag : 'div',
32500             cls : 'roo-document-manager-loading',
32501             cn : [
32502                 {
32503                     tag : 'div',
32504                     tooltip : file.name,
32505                     cls : 'roo-document-manager-thumb',
32506                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32507                 }
32508             ]
32509
32510         });
32511
32512         this.xhr.open(this.method, this.url, true);
32513         
32514         var headers = {
32515             "Accept": "application/json",
32516             "Cache-Control": "no-cache",
32517             "X-Requested-With": "XMLHttpRequest"
32518         };
32519         
32520         for (var headerName in headers) {
32521             var headerValue = headers[headerName];
32522             if (headerValue) {
32523                 this.xhr.setRequestHeader(headerName, headerValue);
32524             }
32525         }
32526         
32527         var _this = this;
32528         
32529         this.xhr.onload = function()
32530         {
32531             _this.xhrOnLoad(_this.xhr);
32532         }
32533         
32534         this.xhr.onerror = function()
32535         {
32536             _this.xhrOnError(_this.xhr);
32537         }
32538         
32539         var formData = new FormData();
32540
32541         formData.append('returnHTML', 'NO');
32542         
32543         formData.append('crop', crop);
32544         
32545         if(typeof(file.filename) != 'undefined'){
32546             formData.append('filename', file.filename);
32547         }
32548         
32549         if(typeof(file.mimetype) != 'undefined'){
32550             formData.append('mimetype', file.mimetype);
32551         }
32552         
32553         Roo.log(formData);
32554         
32555         if(this.fireEvent('prepare', this, formData) != false){
32556             this.xhr.send(formData);
32557         };
32558     }
32559 });
32560
32561 /*
32562 * Licence: LGPL
32563 */
32564
32565 /**
32566  * @class Roo.bootstrap.DocumentViewer
32567  * @extends Roo.bootstrap.Component
32568  * Bootstrap DocumentViewer class
32569  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32570  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32571  * 
32572  * @constructor
32573  * Create a new DocumentViewer
32574  * @param {Object} config The config object
32575  */
32576
32577 Roo.bootstrap.DocumentViewer = function(config){
32578     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32579     
32580     this.addEvents({
32581         /**
32582          * @event initial
32583          * Fire after initEvent
32584          * @param {Roo.bootstrap.DocumentViewer} this
32585          */
32586         "initial" : true,
32587         /**
32588          * @event click
32589          * Fire after click
32590          * @param {Roo.bootstrap.DocumentViewer} this
32591          */
32592         "click" : true,
32593         /**
32594          * @event download
32595          * Fire after download button
32596          * @param {Roo.bootstrap.DocumentViewer} this
32597          */
32598         "download" : true,
32599         /**
32600          * @event trash
32601          * Fire after trash button
32602          * @param {Roo.bootstrap.DocumentViewer} this
32603          */
32604         "trash" : true
32605         
32606     });
32607 };
32608
32609 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32610     
32611     showDownload : true,
32612     
32613     showTrash : true,
32614     
32615     getAutoCreate : function()
32616     {
32617         var cfg = {
32618             tag : 'div',
32619             cls : 'roo-document-viewer',
32620             cn : [
32621                 {
32622                     tag : 'div',
32623                     cls : 'roo-document-viewer-body',
32624                     cn : [
32625                         {
32626                             tag : 'div',
32627                             cls : 'roo-document-viewer-thumb',
32628                             cn : [
32629                                 {
32630                                     tag : 'img',
32631                                     cls : 'roo-document-viewer-image'
32632                                 }
32633                             ]
32634                         }
32635                     ]
32636                 },
32637                 {
32638                     tag : 'div',
32639                     cls : 'roo-document-viewer-footer',
32640                     cn : {
32641                         tag : 'div',
32642                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32643                         cn : [
32644                             {
32645                                 tag : 'div',
32646                                 cls : 'btn-group roo-document-viewer-download',
32647                                 cn : [
32648                                     {
32649                                         tag : 'button',
32650                                         cls : 'btn btn-default',
32651                                         html : '<i class="fa fa-download"></i>'
32652                                     }
32653                                 ]
32654                             },
32655                             {
32656                                 tag : 'div',
32657                                 cls : 'btn-group roo-document-viewer-trash',
32658                                 cn : [
32659                                     {
32660                                         tag : 'button',
32661                                         cls : 'btn btn-default',
32662                                         html : '<i class="fa fa-trash"></i>'
32663                                     }
32664                                 ]
32665                             }
32666                         ]
32667                     }
32668                 }
32669             ]
32670         };
32671         
32672         return cfg;
32673     },
32674     
32675     initEvents : function()
32676     {
32677         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32678         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32679         
32680         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32681         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32682         
32683         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32684         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32685         
32686         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32687         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32688         
32689         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32690         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32691         
32692         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32693         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32694         
32695         this.bodyEl.on('click', this.onClick, this);
32696         this.downloadBtn.on('click', this.onDownload, this);
32697         this.trashBtn.on('click', this.onTrash, this);
32698         
32699         this.downloadBtn.hide();
32700         this.trashBtn.hide();
32701         
32702         if(this.showDownload){
32703             this.downloadBtn.show();
32704         }
32705         
32706         if(this.showTrash){
32707             this.trashBtn.show();
32708         }
32709         
32710         if(!this.showDownload && !this.showTrash) {
32711             this.footerEl.hide();
32712         }
32713         
32714     },
32715     
32716     initial : function()
32717     {
32718         this.fireEvent('initial', this);
32719         
32720     },
32721     
32722     onClick : function(e)
32723     {
32724         e.preventDefault();
32725         
32726         this.fireEvent('click', this);
32727     },
32728     
32729     onDownload : function(e)
32730     {
32731         e.preventDefault();
32732         
32733         this.fireEvent('download', this);
32734     },
32735     
32736     onTrash : function(e)
32737     {
32738         e.preventDefault();
32739         
32740         this.fireEvent('trash', this);
32741     }
32742     
32743 });
32744 /*
32745  * - LGPL
32746  *
32747  * nav progress bar
32748  * 
32749  */
32750
32751 /**
32752  * @class Roo.bootstrap.NavProgressBar
32753  * @extends Roo.bootstrap.Component
32754  * Bootstrap NavProgressBar class
32755  * 
32756  * @constructor
32757  * Create a new nav progress bar
32758  * @param {Object} config The config object
32759  */
32760
32761 Roo.bootstrap.NavProgressBar = function(config){
32762     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32763
32764     this.bullets = this.bullets || [];
32765    
32766 //    Roo.bootstrap.NavProgressBar.register(this);
32767      this.addEvents({
32768         /**
32769              * @event changed
32770              * Fires when the active item changes
32771              * @param {Roo.bootstrap.NavProgressBar} this
32772              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32773              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32774          */
32775         'changed': true
32776      });
32777     
32778 };
32779
32780 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32781     
32782     bullets : [],
32783     barItems : [],
32784     
32785     getAutoCreate : function()
32786     {
32787         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32788         
32789         cfg = {
32790             tag : 'div',
32791             cls : 'roo-navigation-bar-group',
32792             cn : [
32793                 {
32794                     tag : 'div',
32795                     cls : 'roo-navigation-top-bar'
32796                 },
32797                 {
32798                     tag : 'div',
32799                     cls : 'roo-navigation-bullets-bar',
32800                     cn : [
32801                         {
32802                             tag : 'ul',
32803                             cls : 'roo-navigation-bar'
32804                         }
32805                     ]
32806                 },
32807                 
32808                 {
32809                     tag : 'div',
32810                     cls : 'roo-navigation-bottom-bar'
32811                 }
32812             ]
32813             
32814         };
32815         
32816         return cfg;
32817         
32818     },
32819     
32820     initEvents: function() 
32821     {
32822         
32823     },
32824     
32825     onRender : function(ct, position) 
32826     {
32827         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32828         
32829         if(this.bullets.length){
32830             Roo.each(this.bullets, function(b){
32831                this.addItem(b);
32832             }, this);
32833         }
32834         
32835         this.format();
32836         
32837     },
32838     
32839     addItem : function(cfg)
32840     {
32841         var item = new Roo.bootstrap.NavProgressItem(cfg);
32842         
32843         item.parentId = this.id;
32844         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32845         
32846         if(cfg.html){
32847             var top = new Roo.bootstrap.Element({
32848                 tag : 'div',
32849                 cls : 'roo-navigation-bar-text'
32850             });
32851             
32852             var bottom = new Roo.bootstrap.Element({
32853                 tag : 'div',
32854                 cls : 'roo-navigation-bar-text'
32855             });
32856             
32857             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32858             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32859             
32860             var topText = new Roo.bootstrap.Element({
32861                 tag : 'span',
32862                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32863             });
32864             
32865             var bottomText = new Roo.bootstrap.Element({
32866                 tag : 'span',
32867                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32868             });
32869             
32870             topText.onRender(top.el, null);
32871             bottomText.onRender(bottom.el, null);
32872             
32873             item.topEl = top;
32874             item.bottomEl = bottom;
32875         }
32876         
32877         this.barItems.push(item);
32878         
32879         return item;
32880     },
32881     
32882     getActive : function()
32883     {
32884         var active = false;
32885         
32886         Roo.each(this.barItems, function(v){
32887             
32888             if (!v.isActive()) {
32889                 return;
32890             }
32891             
32892             active = v;
32893             return false;
32894             
32895         });
32896         
32897         return active;
32898     },
32899     
32900     setActiveItem : function(item)
32901     {
32902         var prev = false;
32903         
32904         Roo.each(this.barItems, function(v){
32905             if (v.rid == item.rid) {
32906                 return ;
32907             }
32908             
32909             if (v.isActive()) {
32910                 v.setActive(false);
32911                 prev = v;
32912             }
32913         });
32914
32915         item.setActive(true);
32916         
32917         this.fireEvent('changed', this, item, prev);
32918     },
32919     
32920     getBarItem: function(rid)
32921     {
32922         var ret = false;
32923         
32924         Roo.each(this.barItems, function(e) {
32925             if (e.rid != rid) {
32926                 return;
32927             }
32928             
32929             ret =  e;
32930             return false;
32931         });
32932         
32933         return ret;
32934     },
32935     
32936     indexOfItem : function(item)
32937     {
32938         var index = false;
32939         
32940         Roo.each(this.barItems, function(v, i){
32941             
32942             if (v.rid != item.rid) {
32943                 return;
32944             }
32945             
32946             index = i;
32947             return false
32948         });
32949         
32950         return index;
32951     },
32952     
32953     setActiveNext : function()
32954     {
32955         var i = this.indexOfItem(this.getActive());
32956         
32957         if (i > this.barItems.length) {
32958             return;
32959         }
32960         
32961         this.setActiveItem(this.barItems[i+1]);
32962     },
32963     
32964     setActivePrev : function()
32965     {
32966         var i = this.indexOfItem(this.getActive());
32967         
32968         if (i  < 1) {
32969             return;
32970         }
32971         
32972         this.setActiveItem(this.barItems[i-1]);
32973     },
32974     
32975     format : function()
32976     {
32977         if(!this.barItems.length){
32978             return;
32979         }
32980      
32981         var width = 100 / this.barItems.length;
32982         
32983         Roo.each(this.barItems, function(i){
32984             i.el.setStyle('width', width + '%');
32985             i.topEl.el.setStyle('width', width + '%');
32986             i.bottomEl.el.setStyle('width', width + '%');
32987         }, this);
32988         
32989     }
32990     
32991 });
32992 /*
32993  * - LGPL
32994  *
32995  * Nav Progress Item
32996  * 
32997  */
32998
32999 /**
33000  * @class Roo.bootstrap.NavProgressItem
33001  * @extends Roo.bootstrap.Component
33002  * Bootstrap NavProgressItem class
33003  * @cfg {String} rid the reference id
33004  * @cfg {Boolean} active (true|false) Is item active default false
33005  * @cfg {Boolean} disabled (true|false) Is item active default false
33006  * @cfg {String} html
33007  * @cfg {String} position (top|bottom) text position default bottom
33008  * @cfg {String} icon show icon instead of number
33009  * 
33010  * @constructor
33011  * Create a new NavProgressItem
33012  * @param {Object} config The config object
33013  */
33014 Roo.bootstrap.NavProgressItem = function(config){
33015     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33016     this.addEvents({
33017         // raw events
33018         /**
33019          * @event click
33020          * The raw click event for the entire grid.
33021          * @param {Roo.bootstrap.NavProgressItem} this
33022          * @param {Roo.EventObject} e
33023          */
33024         "click" : true
33025     });
33026    
33027 };
33028
33029 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33030     
33031     rid : '',
33032     active : false,
33033     disabled : false,
33034     html : '',
33035     position : 'bottom',
33036     icon : false,
33037     
33038     getAutoCreate : function()
33039     {
33040         var iconCls = 'roo-navigation-bar-item-icon';
33041         
33042         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33043         
33044         var cfg = {
33045             tag: 'li',
33046             cls: 'roo-navigation-bar-item',
33047             cn : [
33048                 {
33049                     tag : 'i',
33050                     cls : iconCls
33051                 }
33052             ]
33053         };
33054         
33055         if(this.active){
33056             cfg.cls += ' active';
33057         }
33058         if(this.disabled){
33059             cfg.cls += ' disabled';
33060         }
33061         
33062         return cfg;
33063     },
33064     
33065     disable : function()
33066     {
33067         this.setDisabled(true);
33068     },
33069     
33070     enable : function()
33071     {
33072         this.setDisabled(false);
33073     },
33074     
33075     initEvents: function() 
33076     {
33077         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33078         
33079         this.iconEl.on('click', this.onClick, this);
33080     },
33081     
33082     onClick : function(e)
33083     {
33084         e.preventDefault();
33085         
33086         if(this.disabled){
33087             return;
33088         }
33089         
33090         if(this.fireEvent('click', this, e) === false){
33091             return;
33092         };
33093         
33094         this.parent().setActiveItem(this);
33095     },
33096     
33097     isActive: function () 
33098     {
33099         return this.active;
33100     },
33101     
33102     setActive : function(state)
33103     {
33104         if(this.active == state){
33105             return;
33106         }
33107         
33108         this.active = state;
33109         
33110         if (state) {
33111             this.el.addClass('active');
33112             return;
33113         }
33114         
33115         this.el.removeClass('active');
33116         
33117         return;
33118     },
33119     
33120     setDisabled : function(state)
33121     {
33122         if(this.disabled == state){
33123             return;
33124         }
33125         
33126         this.disabled = state;
33127         
33128         if (state) {
33129             this.el.addClass('disabled');
33130             return;
33131         }
33132         
33133         this.el.removeClass('disabled');
33134     },
33135     
33136     tooltipEl : function()
33137     {
33138         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33139     }
33140 });
33141  
33142
33143  /*
33144  * - LGPL
33145  *
33146  * FieldLabel
33147  * 
33148  */
33149
33150 /**
33151  * @class Roo.bootstrap.FieldLabel
33152  * @extends Roo.bootstrap.Component
33153  * Bootstrap FieldLabel class
33154  * @cfg {String} html contents of the element
33155  * @cfg {String} tag tag of the element default label
33156  * @cfg {String} cls class of the element
33157  * @cfg {String} target label target 
33158  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33159  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33160  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33161  * @cfg {String} iconTooltip default "This field is required"
33162  * @cfg {String} indicatorpos (left|right) default left
33163  * 
33164  * @constructor
33165  * Create a new FieldLabel
33166  * @param {Object} config The config object
33167  */
33168
33169 Roo.bootstrap.FieldLabel = function(config){
33170     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33171     
33172     this.addEvents({
33173             /**
33174              * @event invalid
33175              * Fires after the field has been marked as invalid.
33176              * @param {Roo.form.FieldLabel} this
33177              * @param {String} msg The validation message
33178              */
33179             invalid : true,
33180             /**
33181              * @event valid
33182              * Fires after the field has been validated with no errors.
33183              * @param {Roo.form.FieldLabel} this
33184              */
33185             valid : true
33186         });
33187 };
33188
33189 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33190     
33191     tag: 'label',
33192     cls: '',
33193     html: '',
33194     target: '',
33195     allowBlank : true,
33196     invalidClass : 'has-warning',
33197     validClass : 'has-success',
33198     iconTooltip : 'This field is required',
33199     indicatorpos : 'left',
33200     
33201     getAutoCreate : function(){
33202         
33203         var cls = "";
33204         if (!this.allowBlank) {
33205             cls  = "visible";
33206         }
33207         
33208         var cfg = {
33209             tag : this.tag,
33210             cls : 'roo-bootstrap-field-label ' + this.cls,
33211             for : this.target,
33212             cn : [
33213                 {
33214                     tag : 'i',
33215                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33216                     tooltip : this.iconTooltip
33217                 },
33218                 {
33219                     tag : 'span',
33220                     html : this.html
33221                 }
33222             ] 
33223         };
33224         
33225         if(this.indicatorpos == 'right'){
33226             var cfg = {
33227                 tag : this.tag,
33228                 cls : 'roo-bootstrap-field-label ' + this.cls,
33229                 for : this.target,
33230                 cn : [
33231                     {
33232                         tag : 'span',
33233                         html : this.html
33234                     },
33235                     {
33236                         tag : 'i',
33237                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33238                         tooltip : this.iconTooltip
33239                     }
33240                 ] 
33241             };
33242         }
33243         
33244         return cfg;
33245     },
33246     
33247     initEvents: function() 
33248     {
33249         Roo.bootstrap.Element.superclass.initEvents.call(this);
33250         
33251         this.indicator = this.indicatorEl();
33252         
33253         if(this.indicator){
33254             this.indicator.removeClass('visible');
33255             this.indicator.addClass('invisible');
33256         }
33257         
33258         Roo.bootstrap.FieldLabel.register(this);
33259     },
33260     
33261     indicatorEl : function()
33262     {
33263         var indicator = this.el.select('i.roo-required-indicator',true).first();
33264         
33265         if(!indicator){
33266             return false;
33267         }
33268         
33269         return indicator;
33270         
33271     },
33272     
33273     /**
33274      * Mark this field as valid
33275      */
33276     markValid : function()
33277     {
33278         if(this.indicator){
33279             this.indicator.removeClass('visible');
33280             this.indicator.addClass('invisible');
33281         }
33282         if (Roo.bootstrap.version == 3) {
33283             this.el.removeClass(this.invalidClass);
33284             this.el.addClass(this.validClass);
33285         } else {
33286             this.el.removeClass('is-invalid');
33287             this.el.addClass('is-valid');
33288         }
33289         
33290         
33291         this.fireEvent('valid', this);
33292     },
33293     
33294     /**
33295      * Mark this field as invalid
33296      * @param {String} msg The validation message
33297      */
33298     markInvalid : function(msg)
33299     {
33300         if(this.indicator){
33301             this.indicator.removeClass('invisible');
33302             this.indicator.addClass('visible');
33303         }
33304           if (Roo.bootstrap.version == 3) {
33305             this.el.removeClass(this.validClass);
33306             this.el.addClass(this.invalidClass);
33307         } else {
33308             this.el.removeClass('is-valid');
33309             this.el.addClass('is-invalid');
33310         }
33311         
33312         
33313         this.fireEvent('invalid', this, msg);
33314     }
33315     
33316    
33317 });
33318
33319 Roo.apply(Roo.bootstrap.FieldLabel, {
33320     
33321     groups: {},
33322     
33323      /**
33324     * register a FieldLabel Group
33325     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33326     */
33327     register : function(label)
33328     {
33329         if(this.groups.hasOwnProperty(label.target)){
33330             return;
33331         }
33332      
33333         this.groups[label.target] = label;
33334         
33335     },
33336     /**
33337     * fetch a FieldLabel Group based on the target
33338     * @param {string} target
33339     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33340     */
33341     get: function(target) {
33342         if (typeof(this.groups[target]) == 'undefined') {
33343             return false;
33344         }
33345         
33346         return this.groups[target] ;
33347     }
33348 });
33349
33350  
33351
33352  /*
33353  * - LGPL
33354  *
33355  * page DateSplitField.
33356  * 
33357  */
33358
33359
33360 /**
33361  * @class Roo.bootstrap.DateSplitField
33362  * @extends Roo.bootstrap.Component
33363  * Bootstrap DateSplitField class
33364  * @cfg {string} fieldLabel - the label associated
33365  * @cfg {Number} labelWidth set the width of label (0-12)
33366  * @cfg {String} labelAlign (top|left)
33367  * @cfg {Boolean} dayAllowBlank (true|false) default false
33368  * @cfg {Boolean} monthAllowBlank (true|false) default false
33369  * @cfg {Boolean} yearAllowBlank (true|false) default false
33370  * @cfg {string} dayPlaceholder 
33371  * @cfg {string} monthPlaceholder
33372  * @cfg {string} yearPlaceholder
33373  * @cfg {string} dayFormat default 'd'
33374  * @cfg {string} monthFormat default 'm'
33375  * @cfg {string} yearFormat default 'Y'
33376  * @cfg {Number} labellg set the width of label (1-12)
33377  * @cfg {Number} labelmd set the width of label (1-12)
33378  * @cfg {Number} labelsm set the width of label (1-12)
33379  * @cfg {Number} labelxs set the width of label (1-12)
33380
33381  *     
33382  * @constructor
33383  * Create a new DateSplitField
33384  * @param {Object} config The config object
33385  */
33386
33387 Roo.bootstrap.DateSplitField = function(config){
33388     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33389     
33390     this.addEvents({
33391         // raw events
33392          /**
33393          * @event years
33394          * getting the data of years
33395          * @param {Roo.bootstrap.DateSplitField} this
33396          * @param {Object} years
33397          */
33398         "years" : true,
33399         /**
33400          * @event days
33401          * getting the data of days
33402          * @param {Roo.bootstrap.DateSplitField} this
33403          * @param {Object} days
33404          */
33405         "days" : true,
33406         /**
33407          * @event invalid
33408          * Fires after the field has been marked as invalid.
33409          * @param {Roo.form.Field} this
33410          * @param {String} msg The validation message
33411          */
33412         invalid : true,
33413        /**
33414          * @event valid
33415          * Fires after the field has been validated with no errors.
33416          * @param {Roo.form.Field} this
33417          */
33418         valid : true
33419     });
33420 };
33421
33422 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33423     
33424     fieldLabel : '',
33425     labelAlign : 'top',
33426     labelWidth : 3,
33427     dayAllowBlank : false,
33428     monthAllowBlank : false,
33429     yearAllowBlank : false,
33430     dayPlaceholder : '',
33431     monthPlaceholder : '',
33432     yearPlaceholder : '',
33433     dayFormat : 'd',
33434     monthFormat : 'm',
33435     yearFormat : 'Y',
33436     isFormField : true,
33437     labellg : 0,
33438     labelmd : 0,
33439     labelsm : 0,
33440     labelxs : 0,
33441     
33442     getAutoCreate : function()
33443     {
33444         var cfg = {
33445             tag : 'div',
33446             cls : 'row roo-date-split-field-group',
33447             cn : [
33448                 {
33449                     tag : 'input',
33450                     type : 'hidden',
33451                     cls : 'form-hidden-field roo-date-split-field-group-value',
33452                     name : this.name
33453                 }
33454             ]
33455         };
33456         
33457         var labelCls = 'col-md-12';
33458         var contentCls = 'col-md-4';
33459         
33460         if(this.fieldLabel){
33461             
33462             var label = {
33463                 tag : 'div',
33464                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33465                 cn : [
33466                     {
33467                         tag : 'label',
33468                         html : this.fieldLabel
33469                     }
33470                 ]
33471             };
33472             
33473             if(this.labelAlign == 'left'){
33474             
33475                 if(this.labelWidth > 12){
33476                     label.style = "width: " + this.labelWidth + 'px';
33477                 }
33478
33479                 if(this.labelWidth < 13 && this.labelmd == 0){
33480                     this.labelmd = this.labelWidth;
33481                 }
33482
33483                 if(this.labellg > 0){
33484                     labelCls = ' col-lg-' + this.labellg;
33485                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33486                 }
33487
33488                 if(this.labelmd > 0){
33489                     labelCls = ' col-md-' + this.labelmd;
33490                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33491                 }
33492
33493                 if(this.labelsm > 0){
33494                     labelCls = ' col-sm-' + this.labelsm;
33495                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33496                 }
33497
33498                 if(this.labelxs > 0){
33499                     labelCls = ' col-xs-' + this.labelxs;
33500                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33501                 }
33502             }
33503             
33504             label.cls += ' ' + labelCls;
33505             
33506             cfg.cn.push(label);
33507         }
33508         
33509         Roo.each(['day', 'month', 'year'], function(t){
33510             cfg.cn.push({
33511                 tag : 'div',
33512                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33513             });
33514         }, this);
33515         
33516         return cfg;
33517     },
33518     
33519     inputEl: function ()
33520     {
33521         return this.el.select('.roo-date-split-field-group-value', true).first();
33522     },
33523     
33524     onRender : function(ct, position) 
33525     {
33526         var _this = this;
33527         
33528         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33529         
33530         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33531         
33532         this.dayField = new Roo.bootstrap.ComboBox({
33533             allowBlank : this.dayAllowBlank,
33534             alwaysQuery : true,
33535             displayField : 'value',
33536             editable : false,
33537             fieldLabel : '',
33538             forceSelection : true,
33539             mode : 'local',
33540             placeholder : this.dayPlaceholder,
33541             selectOnFocus : true,
33542             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33543             triggerAction : 'all',
33544             typeAhead : true,
33545             valueField : 'value',
33546             store : new Roo.data.SimpleStore({
33547                 data : (function() {    
33548                     var days = [];
33549                     _this.fireEvent('days', _this, days);
33550                     return days;
33551                 })(),
33552                 fields : [ 'value' ]
33553             }),
33554             listeners : {
33555                 select : function (_self, record, index)
33556                 {
33557                     _this.setValue(_this.getValue());
33558                 }
33559             }
33560         });
33561
33562         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33563         
33564         this.monthField = new Roo.bootstrap.MonthField({
33565             after : '<i class=\"fa fa-calendar\"></i>',
33566             allowBlank : this.monthAllowBlank,
33567             placeholder : this.monthPlaceholder,
33568             readOnly : true,
33569             listeners : {
33570                 render : function (_self)
33571                 {
33572                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33573                         e.preventDefault();
33574                         _self.focus();
33575                     });
33576                 },
33577                 select : function (_self, oldvalue, newvalue)
33578                 {
33579                     _this.setValue(_this.getValue());
33580                 }
33581             }
33582         });
33583         
33584         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33585         
33586         this.yearField = new Roo.bootstrap.ComboBox({
33587             allowBlank : this.yearAllowBlank,
33588             alwaysQuery : true,
33589             displayField : 'value',
33590             editable : false,
33591             fieldLabel : '',
33592             forceSelection : true,
33593             mode : 'local',
33594             placeholder : this.yearPlaceholder,
33595             selectOnFocus : true,
33596             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33597             triggerAction : 'all',
33598             typeAhead : true,
33599             valueField : 'value',
33600             store : new Roo.data.SimpleStore({
33601                 data : (function() {
33602                     var years = [];
33603                     _this.fireEvent('years', _this, years);
33604                     return years;
33605                 })(),
33606                 fields : [ 'value' ]
33607             }),
33608             listeners : {
33609                 select : function (_self, record, index)
33610                 {
33611                     _this.setValue(_this.getValue());
33612                 }
33613             }
33614         });
33615
33616         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33617     },
33618     
33619     setValue : function(v, format)
33620     {
33621         this.inputEl.dom.value = v;
33622         
33623         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33624         
33625         var d = Date.parseDate(v, f);
33626         
33627         if(!d){
33628             this.validate();
33629             return;
33630         }
33631         
33632         this.setDay(d.format(this.dayFormat));
33633         this.setMonth(d.format(this.monthFormat));
33634         this.setYear(d.format(this.yearFormat));
33635         
33636         this.validate();
33637         
33638         return;
33639     },
33640     
33641     setDay : function(v)
33642     {
33643         this.dayField.setValue(v);
33644         this.inputEl.dom.value = this.getValue();
33645         this.validate();
33646         return;
33647     },
33648     
33649     setMonth : function(v)
33650     {
33651         this.monthField.setValue(v, true);
33652         this.inputEl.dom.value = this.getValue();
33653         this.validate();
33654         return;
33655     },
33656     
33657     setYear : function(v)
33658     {
33659         this.yearField.setValue(v);
33660         this.inputEl.dom.value = this.getValue();
33661         this.validate();
33662         return;
33663     },
33664     
33665     getDay : function()
33666     {
33667         return this.dayField.getValue();
33668     },
33669     
33670     getMonth : function()
33671     {
33672         return this.monthField.getValue();
33673     },
33674     
33675     getYear : function()
33676     {
33677         return this.yearField.getValue();
33678     },
33679     
33680     getValue : function()
33681     {
33682         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33683         
33684         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33685         
33686         return date;
33687     },
33688     
33689     reset : function()
33690     {
33691         this.setDay('');
33692         this.setMonth('');
33693         this.setYear('');
33694         this.inputEl.dom.value = '';
33695         this.validate();
33696         return;
33697     },
33698     
33699     validate : function()
33700     {
33701         var d = this.dayField.validate();
33702         var m = this.monthField.validate();
33703         var y = this.yearField.validate();
33704         
33705         var valid = true;
33706         
33707         if(
33708                 (!this.dayAllowBlank && !d) ||
33709                 (!this.monthAllowBlank && !m) ||
33710                 (!this.yearAllowBlank && !y)
33711         ){
33712             valid = false;
33713         }
33714         
33715         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33716             return valid;
33717         }
33718         
33719         if(valid){
33720             this.markValid();
33721             return valid;
33722         }
33723         
33724         this.markInvalid();
33725         
33726         return valid;
33727     },
33728     
33729     markValid : function()
33730     {
33731         
33732         var label = this.el.select('label', true).first();
33733         var icon = this.el.select('i.fa-star', true).first();
33734
33735         if(label && icon){
33736             icon.remove();
33737         }
33738         
33739         this.fireEvent('valid', this);
33740     },
33741     
33742      /**
33743      * Mark this field as invalid
33744      * @param {String} msg The validation message
33745      */
33746     markInvalid : function(msg)
33747     {
33748         
33749         var label = this.el.select('label', true).first();
33750         var icon = this.el.select('i.fa-star', true).first();
33751
33752         if(label && !icon){
33753             this.el.select('.roo-date-split-field-label', true).createChild({
33754                 tag : 'i',
33755                 cls : 'text-danger fa fa-lg fa-star',
33756                 tooltip : 'This field is required',
33757                 style : 'margin-right:5px;'
33758             }, label, true);
33759         }
33760         
33761         this.fireEvent('invalid', this, msg);
33762     },
33763     
33764     clearInvalid : function()
33765     {
33766         var label = this.el.select('label', true).first();
33767         var icon = this.el.select('i.fa-star', true).first();
33768
33769         if(label && icon){
33770             icon.remove();
33771         }
33772         
33773         this.fireEvent('valid', this);
33774     },
33775     
33776     getName: function()
33777     {
33778         return this.name;
33779     }
33780     
33781 });
33782
33783  /**
33784  *
33785  * This is based on 
33786  * http://masonry.desandro.com
33787  *
33788  * The idea is to render all the bricks based on vertical width...
33789  *
33790  * The original code extends 'outlayer' - we might need to use that....
33791  * 
33792  */
33793
33794
33795 /**
33796  * @class Roo.bootstrap.LayoutMasonry
33797  * @extends Roo.bootstrap.Component
33798  * Bootstrap Layout Masonry class
33799  * 
33800  * @constructor
33801  * Create a new Element
33802  * @param {Object} config The config object
33803  */
33804
33805 Roo.bootstrap.LayoutMasonry = function(config){
33806     
33807     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33808     
33809     this.bricks = [];
33810     
33811     Roo.bootstrap.LayoutMasonry.register(this);
33812     
33813     this.addEvents({
33814         // raw events
33815         /**
33816          * @event layout
33817          * Fire after layout the items
33818          * @param {Roo.bootstrap.LayoutMasonry} this
33819          * @param {Roo.EventObject} e
33820          */
33821         "layout" : true
33822     });
33823     
33824 };
33825
33826 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33827     
33828     /**
33829      * @cfg {Boolean} isLayoutInstant = no animation?
33830      */   
33831     isLayoutInstant : false, // needed?
33832    
33833     /**
33834      * @cfg {Number} boxWidth  width of the columns
33835      */   
33836     boxWidth : 450,
33837     
33838       /**
33839      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33840      */   
33841     boxHeight : 0,
33842     
33843     /**
33844      * @cfg {Number} padWidth padding below box..
33845      */   
33846     padWidth : 10, 
33847     
33848     /**
33849      * @cfg {Number} gutter gutter width..
33850      */   
33851     gutter : 10,
33852     
33853      /**
33854      * @cfg {Number} maxCols maximum number of columns
33855      */   
33856     
33857     maxCols: 0,
33858     
33859     /**
33860      * @cfg {Boolean} isAutoInitial defalut true
33861      */   
33862     isAutoInitial : true, 
33863     
33864     containerWidth: 0,
33865     
33866     /**
33867      * @cfg {Boolean} isHorizontal defalut false
33868      */   
33869     isHorizontal : false, 
33870
33871     currentSize : null,
33872     
33873     tag: 'div',
33874     
33875     cls: '',
33876     
33877     bricks: null, //CompositeElement
33878     
33879     cols : 1,
33880     
33881     _isLayoutInited : false,
33882     
33883 //    isAlternative : false, // only use for vertical layout...
33884     
33885     /**
33886      * @cfg {Number} alternativePadWidth padding below box..
33887      */   
33888     alternativePadWidth : 50,
33889     
33890     selectedBrick : [],
33891     
33892     getAutoCreate : function(){
33893         
33894         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33895         
33896         var cfg = {
33897             tag: this.tag,
33898             cls: 'blog-masonary-wrapper ' + this.cls,
33899             cn : {
33900                 cls : 'mas-boxes masonary'
33901             }
33902         };
33903         
33904         return cfg;
33905     },
33906     
33907     getChildContainer: function( )
33908     {
33909         if (this.boxesEl) {
33910             return this.boxesEl;
33911         }
33912         
33913         this.boxesEl = this.el.select('.mas-boxes').first();
33914         
33915         return this.boxesEl;
33916     },
33917     
33918     
33919     initEvents : function()
33920     {
33921         var _this = this;
33922         
33923         if(this.isAutoInitial){
33924             Roo.log('hook children rendered');
33925             this.on('childrenrendered', function() {
33926                 Roo.log('children rendered');
33927                 _this.initial();
33928             } ,this);
33929         }
33930     },
33931     
33932     initial : function()
33933     {
33934         this.selectedBrick = [];
33935         
33936         this.currentSize = this.el.getBox(true);
33937         
33938         Roo.EventManager.onWindowResize(this.resize, this); 
33939
33940         if(!this.isAutoInitial){
33941             this.layout();
33942             return;
33943         }
33944         
33945         this.layout();
33946         
33947         return;
33948         //this.layout.defer(500,this);
33949         
33950     },
33951     
33952     resize : function()
33953     {
33954         var cs = this.el.getBox(true);
33955         
33956         if (
33957                 this.currentSize.width == cs.width && 
33958                 this.currentSize.x == cs.x && 
33959                 this.currentSize.height == cs.height && 
33960                 this.currentSize.y == cs.y 
33961         ) {
33962             Roo.log("no change in with or X or Y");
33963             return;
33964         }
33965         
33966         this.currentSize = cs;
33967         
33968         this.layout();
33969         
33970     },
33971     
33972     layout : function()
33973     {   
33974         this._resetLayout();
33975         
33976         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33977         
33978         this.layoutItems( isInstant );
33979       
33980         this._isLayoutInited = true;
33981         
33982         this.fireEvent('layout', this);
33983         
33984     },
33985     
33986     _resetLayout : function()
33987     {
33988         if(this.isHorizontal){
33989             this.horizontalMeasureColumns();
33990             return;
33991         }
33992         
33993         this.verticalMeasureColumns();
33994         
33995     },
33996     
33997     verticalMeasureColumns : function()
33998     {
33999         this.getContainerWidth();
34000         
34001 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34002 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34003 //            return;
34004 //        }
34005         
34006         var boxWidth = this.boxWidth + this.padWidth;
34007         
34008         if(this.containerWidth < this.boxWidth){
34009             boxWidth = this.containerWidth
34010         }
34011         
34012         var containerWidth = this.containerWidth;
34013         
34014         var cols = Math.floor(containerWidth / boxWidth);
34015         
34016         this.cols = Math.max( cols, 1 );
34017         
34018         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34019         
34020         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34021         
34022         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34023         
34024         this.colWidth = boxWidth + avail - this.padWidth;
34025         
34026         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34027         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34028     },
34029     
34030     horizontalMeasureColumns : function()
34031     {
34032         this.getContainerWidth();
34033         
34034         var boxWidth = this.boxWidth;
34035         
34036         if(this.containerWidth < boxWidth){
34037             boxWidth = this.containerWidth;
34038         }
34039         
34040         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34041         
34042         this.el.setHeight(boxWidth);
34043         
34044     },
34045     
34046     getContainerWidth : function()
34047     {
34048         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34049     },
34050     
34051     layoutItems : function( isInstant )
34052     {
34053         Roo.log(this.bricks);
34054         
34055         var items = Roo.apply([], this.bricks);
34056         
34057         if(this.isHorizontal){
34058             this._horizontalLayoutItems( items , isInstant );
34059             return;
34060         }
34061         
34062 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34063 //            this._verticalAlternativeLayoutItems( items , isInstant );
34064 //            return;
34065 //        }
34066         
34067         this._verticalLayoutItems( items , isInstant );
34068         
34069     },
34070     
34071     _verticalLayoutItems : function ( items , isInstant)
34072     {
34073         if ( !items || !items.length ) {
34074             return;
34075         }
34076         
34077         var standard = [
34078             ['xs', 'xs', 'xs', 'tall'],
34079             ['xs', 'xs', 'tall'],
34080             ['xs', 'xs', 'sm'],
34081             ['xs', 'xs', 'xs'],
34082             ['xs', 'tall'],
34083             ['xs', 'sm'],
34084             ['xs', 'xs'],
34085             ['xs'],
34086             
34087             ['sm', 'xs', 'xs'],
34088             ['sm', 'xs'],
34089             ['sm'],
34090             
34091             ['tall', 'xs', 'xs', 'xs'],
34092             ['tall', 'xs', 'xs'],
34093             ['tall', 'xs'],
34094             ['tall']
34095             
34096         ];
34097         
34098         var queue = [];
34099         
34100         var boxes = [];
34101         
34102         var box = [];
34103         
34104         Roo.each(items, function(item, k){
34105             
34106             switch (item.size) {
34107                 // these layouts take up a full box,
34108                 case 'md' :
34109                 case 'md-left' :
34110                 case 'md-right' :
34111                 case 'wide' :
34112                     
34113                     if(box.length){
34114                         boxes.push(box);
34115                         box = [];
34116                     }
34117                     
34118                     boxes.push([item]);
34119                     
34120                     break;
34121                     
34122                 case 'xs' :
34123                 case 'sm' :
34124                 case 'tall' :
34125                     
34126                     box.push(item);
34127                     
34128                     break;
34129                 default :
34130                     break;
34131                     
34132             }
34133             
34134         }, this);
34135         
34136         if(box.length){
34137             boxes.push(box);
34138             box = [];
34139         }
34140         
34141         var filterPattern = function(box, length)
34142         {
34143             if(!box.length){
34144                 return;
34145             }
34146             
34147             var match = false;
34148             
34149             var pattern = box.slice(0, length);
34150             
34151             var format = [];
34152             
34153             Roo.each(pattern, function(i){
34154                 format.push(i.size);
34155             }, this);
34156             
34157             Roo.each(standard, function(s){
34158                 
34159                 if(String(s) != String(format)){
34160                     return;
34161                 }
34162                 
34163                 match = true;
34164                 return false;
34165                 
34166             }, this);
34167             
34168             if(!match && length == 1){
34169                 return;
34170             }
34171             
34172             if(!match){
34173                 filterPattern(box, length - 1);
34174                 return;
34175             }
34176                 
34177             queue.push(pattern);
34178
34179             box = box.slice(length, box.length);
34180
34181             filterPattern(box, 4);
34182
34183             return;
34184             
34185         }
34186         
34187         Roo.each(boxes, function(box, k){
34188             
34189             if(!box.length){
34190                 return;
34191             }
34192             
34193             if(box.length == 1){
34194                 queue.push(box);
34195                 return;
34196             }
34197             
34198             filterPattern(box, 4);
34199             
34200         }, this);
34201         
34202         this._processVerticalLayoutQueue( queue, isInstant );
34203         
34204     },
34205     
34206 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34207 //    {
34208 //        if ( !items || !items.length ) {
34209 //            return;
34210 //        }
34211 //
34212 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34213 //        
34214 //    },
34215     
34216     _horizontalLayoutItems : function ( items , isInstant)
34217     {
34218         if ( !items || !items.length || items.length < 3) {
34219             return;
34220         }
34221         
34222         items.reverse();
34223         
34224         var eItems = items.slice(0, 3);
34225         
34226         items = items.slice(3, items.length);
34227         
34228         var standard = [
34229             ['xs', 'xs', 'xs', 'wide'],
34230             ['xs', 'xs', 'wide'],
34231             ['xs', 'xs', 'sm'],
34232             ['xs', 'xs', 'xs'],
34233             ['xs', 'wide'],
34234             ['xs', 'sm'],
34235             ['xs', 'xs'],
34236             ['xs'],
34237             
34238             ['sm', 'xs', 'xs'],
34239             ['sm', 'xs'],
34240             ['sm'],
34241             
34242             ['wide', 'xs', 'xs', 'xs'],
34243             ['wide', 'xs', 'xs'],
34244             ['wide', 'xs'],
34245             ['wide'],
34246             
34247             ['wide-thin']
34248         ];
34249         
34250         var queue = [];
34251         
34252         var boxes = [];
34253         
34254         var box = [];
34255         
34256         Roo.each(items, function(item, k){
34257             
34258             switch (item.size) {
34259                 case 'md' :
34260                 case 'md-left' :
34261                 case 'md-right' :
34262                 case 'tall' :
34263                     
34264                     if(box.length){
34265                         boxes.push(box);
34266                         box = [];
34267                     }
34268                     
34269                     boxes.push([item]);
34270                     
34271                     break;
34272                     
34273                 case 'xs' :
34274                 case 'sm' :
34275                 case 'wide' :
34276                 case 'wide-thin' :
34277                     
34278                     box.push(item);
34279                     
34280                     break;
34281                 default :
34282                     break;
34283                     
34284             }
34285             
34286         }, this);
34287         
34288         if(box.length){
34289             boxes.push(box);
34290             box = [];
34291         }
34292         
34293         var filterPattern = function(box, length)
34294         {
34295             if(!box.length){
34296                 return;
34297             }
34298             
34299             var match = false;
34300             
34301             var pattern = box.slice(0, length);
34302             
34303             var format = [];
34304             
34305             Roo.each(pattern, function(i){
34306                 format.push(i.size);
34307             }, this);
34308             
34309             Roo.each(standard, function(s){
34310                 
34311                 if(String(s) != String(format)){
34312                     return;
34313                 }
34314                 
34315                 match = true;
34316                 return false;
34317                 
34318             }, this);
34319             
34320             if(!match && length == 1){
34321                 return;
34322             }
34323             
34324             if(!match){
34325                 filterPattern(box, length - 1);
34326                 return;
34327             }
34328                 
34329             queue.push(pattern);
34330
34331             box = box.slice(length, box.length);
34332
34333             filterPattern(box, 4);
34334
34335             return;
34336             
34337         }
34338         
34339         Roo.each(boxes, function(box, k){
34340             
34341             if(!box.length){
34342                 return;
34343             }
34344             
34345             if(box.length == 1){
34346                 queue.push(box);
34347                 return;
34348             }
34349             
34350             filterPattern(box, 4);
34351             
34352         }, this);
34353         
34354         
34355         var prune = [];
34356         
34357         var pos = this.el.getBox(true);
34358         
34359         var minX = pos.x;
34360         
34361         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34362         
34363         var hit_end = false;
34364         
34365         Roo.each(queue, function(box){
34366             
34367             if(hit_end){
34368                 
34369                 Roo.each(box, function(b){
34370                 
34371                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34372                     b.el.hide();
34373
34374                 }, this);
34375
34376                 return;
34377             }
34378             
34379             var mx = 0;
34380             
34381             Roo.each(box, function(b){
34382                 
34383                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34384                 b.el.show();
34385
34386                 mx = Math.max(mx, b.x);
34387                 
34388             }, this);
34389             
34390             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34391             
34392             if(maxX < minX){
34393                 
34394                 Roo.each(box, function(b){
34395                 
34396                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34397                     b.el.hide();
34398                     
34399                 }, this);
34400                 
34401                 hit_end = true;
34402                 
34403                 return;
34404             }
34405             
34406             prune.push(box);
34407             
34408         }, this);
34409         
34410         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34411     },
34412     
34413     /** Sets position of item in DOM
34414     * @param {Element} item
34415     * @param {Number} x - horizontal position
34416     * @param {Number} y - vertical position
34417     * @param {Boolean} isInstant - disables transitions
34418     */
34419     _processVerticalLayoutQueue : function( queue, isInstant )
34420     {
34421         var pos = this.el.getBox(true);
34422         var x = pos.x;
34423         var y = pos.y;
34424         var maxY = [];
34425         
34426         for (var i = 0; i < this.cols; i++){
34427             maxY[i] = pos.y;
34428         }
34429         
34430         Roo.each(queue, function(box, k){
34431             
34432             var col = k % this.cols;
34433             
34434             Roo.each(box, function(b,kk){
34435                 
34436                 b.el.position('absolute');
34437                 
34438                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34439                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34440                 
34441                 if(b.size == 'md-left' || b.size == 'md-right'){
34442                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34443                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34444                 }
34445                 
34446                 b.el.setWidth(width);
34447                 b.el.setHeight(height);
34448                 // iframe?
34449                 b.el.select('iframe',true).setSize(width,height);
34450                 
34451             }, this);
34452             
34453             for (var i = 0; i < this.cols; i++){
34454                 
34455                 if(maxY[i] < maxY[col]){
34456                     col = i;
34457                     continue;
34458                 }
34459                 
34460                 col = Math.min(col, i);
34461                 
34462             }
34463             
34464             x = pos.x + col * (this.colWidth + this.padWidth);
34465             
34466             y = maxY[col];
34467             
34468             var positions = [];
34469             
34470             switch (box.length){
34471                 case 1 :
34472                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34473                     break;
34474                 case 2 :
34475                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34476                     break;
34477                 case 3 :
34478                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34479                     break;
34480                 case 4 :
34481                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34482                     break;
34483                 default :
34484                     break;
34485             }
34486             
34487             Roo.each(box, function(b,kk){
34488                 
34489                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34490                 
34491                 var sz = b.el.getSize();
34492                 
34493                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34494                 
34495             }, this);
34496             
34497         }, this);
34498         
34499         var mY = 0;
34500         
34501         for (var i = 0; i < this.cols; i++){
34502             mY = Math.max(mY, maxY[i]);
34503         }
34504         
34505         this.el.setHeight(mY - pos.y);
34506         
34507     },
34508     
34509 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34510 //    {
34511 //        var pos = this.el.getBox(true);
34512 //        var x = pos.x;
34513 //        var y = pos.y;
34514 //        var maxX = pos.right;
34515 //        
34516 //        var maxHeight = 0;
34517 //        
34518 //        Roo.each(items, function(item, k){
34519 //            
34520 //            var c = k % 2;
34521 //            
34522 //            item.el.position('absolute');
34523 //                
34524 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34525 //
34526 //            item.el.setWidth(width);
34527 //
34528 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34529 //
34530 //            item.el.setHeight(height);
34531 //            
34532 //            if(c == 0){
34533 //                item.el.setXY([x, y], isInstant ? false : true);
34534 //            } else {
34535 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34536 //            }
34537 //            
34538 //            y = y + height + this.alternativePadWidth;
34539 //            
34540 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34541 //            
34542 //        }, this);
34543 //        
34544 //        this.el.setHeight(maxHeight);
34545 //        
34546 //    },
34547     
34548     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34549     {
34550         var pos = this.el.getBox(true);
34551         
34552         var minX = pos.x;
34553         var minY = pos.y;
34554         
34555         var maxX = pos.right;
34556         
34557         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34558         
34559         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34560         
34561         Roo.each(queue, function(box, k){
34562             
34563             Roo.each(box, function(b, kk){
34564                 
34565                 b.el.position('absolute');
34566                 
34567                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34568                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34569                 
34570                 if(b.size == 'md-left' || b.size == 'md-right'){
34571                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34572                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34573                 }
34574                 
34575                 b.el.setWidth(width);
34576                 b.el.setHeight(height);
34577                 
34578             }, this);
34579             
34580             if(!box.length){
34581                 return;
34582             }
34583             
34584             var positions = [];
34585             
34586             switch (box.length){
34587                 case 1 :
34588                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34589                     break;
34590                 case 2 :
34591                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34592                     break;
34593                 case 3 :
34594                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34595                     break;
34596                 case 4 :
34597                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34598                     break;
34599                 default :
34600                     break;
34601             }
34602             
34603             Roo.each(box, function(b,kk){
34604                 
34605                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34606                 
34607                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34608                 
34609             }, this);
34610             
34611         }, this);
34612         
34613     },
34614     
34615     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34616     {
34617         Roo.each(eItems, function(b,k){
34618             
34619             b.size = (k == 0) ? 'sm' : 'xs';
34620             b.x = (k == 0) ? 2 : 1;
34621             b.y = (k == 0) ? 2 : 1;
34622             
34623             b.el.position('absolute');
34624             
34625             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34626                 
34627             b.el.setWidth(width);
34628             
34629             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34630             
34631             b.el.setHeight(height);
34632             
34633         }, this);
34634
34635         var positions = [];
34636         
34637         positions.push({
34638             x : maxX - this.unitWidth * 2 - this.gutter,
34639             y : minY
34640         });
34641         
34642         positions.push({
34643             x : maxX - this.unitWidth,
34644             y : minY + (this.unitWidth + this.gutter) * 2
34645         });
34646         
34647         positions.push({
34648             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34649             y : minY
34650         });
34651         
34652         Roo.each(eItems, function(b,k){
34653             
34654             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34655
34656         }, this);
34657         
34658     },
34659     
34660     getVerticalOneBoxColPositions : function(x, y, box)
34661     {
34662         var pos = [];
34663         
34664         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34665         
34666         if(box[0].size == 'md-left'){
34667             rand = 0;
34668         }
34669         
34670         if(box[0].size == 'md-right'){
34671             rand = 1;
34672         }
34673         
34674         pos.push({
34675             x : x + (this.unitWidth + this.gutter) * rand,
34676             y : y
34677         });
34678         
34679         return pos;
34680     },
34681     
34682     getVerticalTwoBoxColPositions : function(x, y, box)
34683     {
34684         var pos = [];
34685         
34686         if(box[0].size == 'xs'){
34687             
34688             pos.push({
34689                 x : x,
34690                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34691             });
34692
34693             pos.push({
34694                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34695                 y : y
34696             });
34697             
34698             return pos;
34699             
34700         }
34701         
34702         pos.push({
34703             x : x,
34704             y : y
34705         });
34706
34707         pos.push({
34708             x : x + (this.unitWidth + this.gutter) * 2,
34709             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34710         });
34711         
34712         return pos;
34713         
34714     },
34715     
34716     getVerticalThreeBoxColPositions : function(x, y, box)
34717     {
34718         var pos = [];
34719         
34720         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34721             
34722             pos.push({
34723                 x : x,
34724                 y : y
34725             });
34726
34727             pos.push({
34728                 x : x + (this.unitWidth + this.gutter) * 1,
34729                 y : y
34730             });
34731             
34732             pos.push({
34733                 x : x + (this.unitWidth + this.gutter) * 2,
34734                 y : y
34735             });
34736             
34737             return pos;
34738             
34739         }
34740         
34741         if(box[0].size == 'xs' && box[1].size == 'xs'){
34742             
34743             pos.push({
34744                 x : x,
34745                 y : y
34746             });
34747
34748             pos.push({
34749                 x : x,
34750                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34751             });
34752             
34753             pos.push({
34754                 x : x + (this.unitWidth + this.gutter) * 1,
34755                 y : y
34756             });
34757             
34758             return pos;
34759             
34760         }
34761         
34762         pos.push({
34763             x : x,
34764             y : y
34765         });
34766
34767         pos.push({
34768             x : x + (this.unitWidth + this.gutter) * 2,
34769             y : y
34770         });
34771
34772         pos.push({
34773             x : x + (this.unitWidth + this.gutter) * 2,
34774             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34775         });
34776             
34777         return pos;
34778         
34779     },
34780     
34781     getVerticalFourBoxColPositions : function(x, y, box)
34782     {
34783         var pos = [];
34784         
34785         if(box[0].size == 'xs'){
34786             
34787             pos.push({
34788                 x : x,
34789                 y : y
34790             });
34791
34792             pos.push({
34793                 x : x,
34794                 y : y + (this.unitHeight + this.gutter) * 1
34795             });
34796             
34797             pos.push({
34798                 x : x,
34799                 y : y + (this.unitHeight + this.gutter) * 2
34800             });
34801             
34802             pos.push({
34803                 x : x + (this.unitWidth + this.gutter) * 1,
34804                 y : y
34805             });
34806             
34807             return pos;
34808             
34809         }
34810         
34811         pos.push({
34812             x : x,
34813             y : y
34814         });
34815
34816         pos.push({
34817             x : x + (this.unitWidth + this.gutter) * 2,
34818             y : y
34819         });
34820
34821         pos.push({
34822             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34823             y : y + (this.unitHeight + this.gutter) * 1
34824         });
34825
34826         pos.push({
34827             x : x + (this.unitWidth + this.gutter) * 2,
34828             y : y + (this.unitWidth + this.gutter) * 2
34829         });
34830
34831         return pos;
34832         
34833     },
34834     
34835     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34836     {
34837         var pos = [];
34838         
34839         if(box[0].size == 'md-left'){
34840             pos.push({
34841                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34842                 y : minY
34843             });
34844             
34845             return pos;
34846         }
34847         
34848         if(box[0].size == 'md-right'){
34849             pos.push({
34850                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34851                 y : minY + (this.unitWidth + this.gutter) * 1
34852             });
34853             
34854             return pos;
34855         }
34856         
34857         var rand = Math.floor(Math.random() * (4 - box[0].y));
34858         
34859         pos.push({
34860             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34861             y : minY + (this.unitWidth + this.gutter) * rand
34862         });
34863         
34864         return pos;
34865         
34866     },
34867     
34868     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34869     {
34870         var pos = [];
34871         
34872         if(box[0].size == 'xs'){
34873             
34874             pos.push({
34875                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34876                 y : minY
34877             });
34878
34879             pos.push({
34880                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34881                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34882             });
34883             
34884             return pos;
34885             
34886         }
34887         
34888         pos.push({
34889             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34890             y : minY
34891         });
34892
34893         pos.push({
34894             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34895             y : minY + (this.unitWidth + this.gutter) * 2
34896         });
34897         
34898         return pos;
34899         
34900     },
34901     
34902     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34903     {
34904         var pos = [];
34905         
34906         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34907             
34908             pos.push({
34909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34910                 y : minY
34911             });
34912
34913             pos.push({
34914                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34915                 y : minY + (this.unitWidth + this.gutter) * 1
34916             });
34917             
34918             pos.push({
34919                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34920                 y : minY + (this.unitWidth + this.gutter) * 2
34921             });
34922             
34923             return pos;
34924             
34925         }
34926         
34927         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34936                 y : minY
34937             });
34938             
34939             pos.push({
34940                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34941                 y : minY + (this.unitWidth + this.gutter) * 1
34942             });
34943             
34944             return pos;
34945             
34946         }
34947         
34948         pos.push({
34949             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34950             y : minY
34951         });
34952
34953         pos.push({
34954             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34955             y : minY + (this.unitWidth + this.gutter) * 2
34956         });
34957
34958         pos.push({
34959             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34960             y : minY + (this.unitWidth + this.gutter) * 2
34961         });
34962             
34963         return pos;
34964         
34965     },
34966     
34967     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34968     {
34969         var pos = [];
34970         
34971         if(box[0].size == 'xs'){
34972             
34973             pos.push({
34974                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34975                 y : minY
34976             });
34977
34978             pos.push({
34979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34980                 y : minY
34981             });
34982             
34983             pos.push({
34984                 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),
34985                 y : minY
34986             });
34987             
34988             pos.push({
34989                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34990                 y : minY + (this.unitWidth + this.gutter) * 1
34991             });
34992             
34993             return pos;
34994             
34995         }
34996         
34997         pos.push({
34998             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34999             y : minY
35000         });
35001         
35002         pos.push({
35003             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35004             y : minY + (this.unitWidth + this.gutter) * 2
35005         });
35006         
35007         pos.push({
35008             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35009             y : minY + (this.unitWidth + this.gutter) * 2
35010         });
35011         
35012         pos.push({
35013             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),
35014             y : minY + (this.unitWidth + this.gutter) * 2
35015         });
35016
35017         return pos;
35018         
35019     },
35020     
35021     /**
35022     * remove a Masonry Brick
35023     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35024     */
35025     removeBrick : function(brick_id)
35026     {
35027         if (!brick_id) {
35028             return;
35029         }
35030         
35031         for (var i = 0; i<this.bricks.length; i++) {
35032             if (this.bricks[i].id == brick_id) {
35033                 this.bricks.splice(i,1);
35034                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35035                 this.initial();
35036             }
35037         }
35038     },
35039     
35040     /**
35041     * adds a Masonry Brick
35042     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35043     */
35044     addBrick : function(cfg)
35045     {
35046         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35047         //this.register(cn);
35048         cn.parentId = this.id;
35049         cn.render(this.el);
35050         return cn;
35051     },
35052     
35053     /**
35054     * register a Masonry Brick
35055     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35056     */
35057     
35058     register : function(brick)
35059     {
35060         this.bricks.push(brick);
35061         brick.masonryId = this.id;
35062     },
35063     
35064     /**
35065     * clear all the Masonry Brick
35066     */
35067     clearAll : function()
35068     {
35069         this.bricks = [];
35070         //this.getChildContainer().dom.innerHTML = "";
35071         this.el.dom.innerHTML = '';
35072     },
35073     
35074     getSelected : function()
35075     {
35076         if (!this.selectedBrick) {
35077             return false;
35078         }
35079         
35080         return this.selectedBrick;
35081     }
35082 });
35083
35084 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35085     
35086     groups: {},
35087      /**
35088     * register a Masonry Layout
35089     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35090     */
35091     
35092     register : function(layout)
35093     {
35094         this.groups[layout.id] = layout;
35095     },
35096     /**
35097     * fetch a  Masonry Layout based on the masonry layout ID
35098     * @param {string} the masonry layout to add
35099     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35100     */
35101     
35102     get: function(layout_id) {
35103         if (typeof(this.groups[layout_id]) == 'undefined') {
35104             return false;
35105         }
35106         return this.groups[layout_id] ;
35107     }
35108     
35109     
35110     
35111 });
35112
35113  
35114
35115  /**
35116  *
35117  * This is based on 
35118  * http://masonry.desandro.com
35119  *
35120  * The idea is to render all the bricks based on vertical width...
35121  *
35122  * The original code extends 'outlayer' - we might need to use that....
35123  * 
35124  */
35125
35126
35127 /**
35128  * @class Roo.bootstrap.LayoutMasonryAuto
35129  * @extends Roo.bootstrap.Component
35130  * Bootstrap Layout Masonry class
35131  * 
35132  * @constructor
35133  * Create a new Element
35134  * @param {Object} config The config object
35135  */
35136
35137 Roo.bootstrap.LayoutMasonryAuto = function(config){
35138     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35139 };
35140
35141 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35142     
35143       /**
35144      * @cfg {Boolean} isFitWidth  - resize the width..
35145      */   
35146     isFitWidth : false,  // options..
35147     /**
35148      * @cfg {Boolean} isOriginLeft = left align?
35149      */   
35150     isOriginLeft : true,
35151     /**
35152      * @cfg {Boolean} isOriginTop = top align?
35153      */   
35154     isOriginTop : false,
35155     /**
35156      * @cfg {Boolean} isLayoutInstant = no animation?
35157      */   
35158     isLayoutInstant : false, // needed?
35159     /**
35160      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35161      */   
35162     isResizingContainer : true,
35163     /**
35164      * @cfg {Number} columnWidth  width of the columns 
35165      */   
35166     
35167     columnWidth : 0,
35168     
35169     /**
35170      * @cfg {Number} maxCols maximum number of columns
35171      */   
35172     
35173     maxCols: 0,
35174     /**
35175      * @cfg {Number} padHeight padding below box..
35176      */   
35177     
35178     padHeight : 10, 
35179     
35180     /**
35181      * @cfg {Boolean} isAutoInitial defalut true
35182      */   
35183     
35184     isAutoInitial : true, 
35185     
35186     // private?
35187     gutter : 0,
35188     
35189     containerWidth: 0,
35190     initialColumnWidth : 0,
35191     currentSize : null,
35192     
35193     colYs : null, // array.
35194     maxY : 0,
35195     padWidth: 10,
35196     
35197     
35198     tag: 'div',
35199     cls: '',
35200     bricks: null, //CompositeElement
35201     cols : 0, // array?
35202     // element : null, // wrapped now this.el
35203     _isLayoutInited : null, 
35204     
35205     
35206     getAutoCreate : function(){
35207         
35208         var cfg = {
35209             tag: this.tag,
35210             cls: 'blog-masonary-wrapper ' + this.cls,
35211             cn : {
35212                 cls : 'mas-boxes masonary'
35213             }
35214         };
35215         
35216         return cfg;
35217     },
35218     
35219     getChildContainer: function( )
35220     {
35221         if (this.boxesEl) {
35222             return this.boxesEl;
35223         }
35224         
35225         this.boxesEl = this.el.select('.mas-boxes').first();
35226         
35227         return this.boxesEl;
35228     },
35229     
35230     
35231     initEvents : function()
35232     {
35233         var _this = this;
35234         
35235         if(this.isAutoInitial){
35236             Roo.log('hook children rendered');
35237             this.on('childrenrendered', function() {
35238                 Roo.log('children rendered');
35239                 _this.initial();
35240             } ,this);
35241         }
35242         
35243     },
35244     
35245     initial : function()
35246     {
35247         this.reloadItems();
35248
35249         this.currentSize = this.el.getBox(true);
35250
35251         /// was window resize... - let's see if this works..
35252         Roo.EventManager.onWindowResize(this.resize, this); 
35253
35254         if(!this.isAutoInitial){
35255             this.layout();
35256             return;
35257         }
35258         
35259         this.layout.defer(500,this);
35260     },
35261     
35262     reloadItems: function()
35263     {
35264         this.bricks = this.el.select('.masonry-brick', true);
35265         
35266         this.bricks.each(function(b) {
35267             //Roo.log(b.getSize());
35268             if (!b.attr('originalwidth')) {
35269                 b.attr('originalwidth',  b.getSize().width);
35270             }
35271             
35272         });
35273         
35274         Roo.log(this.bricks.elements.length);
35275     },
35276     
35277     resize : function()
35278     {
35279         Roo.log('resize');
35280         var cs = this.el.getBox(true);
35281         
35282         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35283             Roo.log("no change in with or X");
35284             return;
35285         }
35286         this.currentSize = cs;
35287         this.layout();
35288     },
35289     
35290     layout : function()
35291     {
35292          Roo.log('layout');
35293         this._resetLayout();
35294         //this._manageStamps();
35295       
35296         // don't animate first layout
35297         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35298         this.layoutItems( isInstant );
35299       
35300         // flag for initalized
35301         this._isLayoutInited = true;
35302     },
35303     
35304     layoutItems : function( isInstant )
35305     {
35306         //var items = this._getItemsForLayout( this.items );
35307         // original code supports filtering layout items.. we just ignore it..
35308         
35309         this._layoutItems( this.bricks , isInstant );
35310       
35311         this._postLayout();
35312     },
35313     _layoutItems : function ( items , isInstant)
35314     {
35315        //this.fireEvent( 'layout', this, items );
35316     
35317
35318         if ( !items || !items.elements.length ) {
35319           // no items, emit event with empty array
35320             return;
35321         }
35322
35323         var queue = [];
35324         items.each(function(item) {
35325             Roo.log("layout item");
35326             Roo.log(item);
35327             // get x/y object from method
35328             var position = this._getItemLayoutPosition( item );
35329             // enqueue
35330             position.item = item;
35331             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35332             queue.push( position );
35333         }, this);
35334       
35335         this._processLayoutQueue( queue );
35336     },
35337     /** Sets position of item in DOM
35338     * @param {Element} item
35339     * @param {Number} x - horizontal position
35340     * @param {Number} y - vertical position
35341     * @param {Boolean} isInstant - disables transitions
35342     */
35343     _processLayoutQueue : function( queue )
35344     {
35345         for ( var i=0, len = queue.length; i < len; i++ ) {
35346             var obj = queue[i];
35347             obj.item.position('absolute');
35348             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35349         }
35350     },
35351       
35352     
35353     /**
35354     * Any logic you want to do after each layout,
35355     * i.e. size the container
35356     */
35357     _postLayout : function()
35358     {
35359         this.resizeContainer();
35360     },
35361     
35362     resizeContainer : function()
35363     {
35364         if ( !this.isResizingContainer ) {
35365             return;
35366         }
35367         var size = this._getContainerSize();
35368         if ( size ) {
35369             this.el.setSize(size.width,size.height);
35370             this.boxesEl.setSize(size.width,size.height);
35371         }
35372     },
35373     
35374     
35375     
35376     _resetLayout : function()
35377     {
35378         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35379         this.colWidth = this.el.getWidth();
35380         //this.gutter = this.el.getWidth(); 
35381         
35382         this.measureColumns();
35383
35384         // reset column Y
35385         var i = this.cols;
35386         this.colYs = [];
35387         while (i--) {
35388             this.colYs.push( 0 );
35389         }
35390     
35391         this.maxY = 0;
35392     },
35393
35394     measureColumns : function()
35395     {
35396         this.getContainerWidth();
35397       // if columnWidth is 0, default to outerWidth of first item
35398         if ( !this.columnWidth ) {
35399             var firstItem = this.bricks.first();
35400             Roo.log(firstItem);
35401             this.columnWidth  = this.containerWidth;
35402             if (firstItem && firstItem.attr('originalwidth') ) {
35403                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35404             }
35405             // columnWidth fall back to item of first element
35406             Roo.log("set column width?");
35407                         this.initialColumnWidth = this.columnWidth  ;
35408
35409             // if first elem has no width, default to size of container
35410             
35411         }
35412         
35413         
35414         if (this.initialColumnWidth) {
35415             this.columnWidth = this.initialColumnWidth;
35416         }
35417         
35418         
35419             
35420         // column width is fixed at the top - however if container width get's smaller we should
35421         // reduce it...
35422         
35423         // this bit calcs how man columns..
35424             
35425         var columnWidth = this.columnWidth += this.gutter;
35426       
35427         // calculate columns
35428         var containerWidth = this.containerWidth + this.gutter;
35429         
35430         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35431         // fix rounding errors, typically with gutters
35432         var excess = columnWidth - containerWidth % columnWidth;
35433         
35434         
35435         // if overshoot is less than a pixel, round up, otherwise floor it
35436         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35437         cols = Math[ mathMethod ]( cols );
35438         this.cols = Math.max( cols, 1 );
35439         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35440         
35441          // padding positioning..
35442         var totalColWidth = this.cols * this.columnWidth;
35443         var padavail = this.containerWidth - totalColWidth;
35444         // so for 2 columns - we need 3 'pads'
35445         
35446         var padNeeded = (1+this.cols) * this.padWidth;
35447         
35448         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35449         
35450         this.columnWidth += padExtra
35451         //this.padWidth = Math.floor(padavail /  ( this.cols));
35452         
35453         // adjust colum width so that padding is fixed??
35454         
35455         // we have 3 columns ... total = width * 3
35456         // we have X left over... that should be used by 
35457         
35458         //if (this.expandC) {
35459             
35460         //}
35461         
35462         
35463         
35464     },
35465     
35466     getContainerWidth : function()
35467     {
35468        /* // container is parent if fit width
35469         var container = this.isFitWidth ? this.element.parentNode : this.element;
35470         // check that this.size and size are there
35471         // IE8 triggers resize on body size change, so they might not be
35472         
35473         var size = getSize( container );  //FIXME
35474         this.containerWidth = size && size.innerWidth; //FIXME
35475         */
35476          
35477         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35478         
35479     },
35480     
35481     _getItemLayoutPosition : function( item )  // what is item?
35482     {
35483         // we resize the item to our columnWidth..
35484       
35485         item.setWidth(this.columnWidth);
35486         item.autoBoxAdjust  = false;
35487         
35488         var sz = item.getSize();
35489  
35490         // how many columns does this brick span
35491         var remainder = this.containerWidth % this.columnWidth;
35492         
35493         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35494         // round if off by 1 pixel, otherwise use ceil
35495         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35496         colSpan = Math.min( colSpan, this.cols );
35497         
35498         // normally this should be '1' as we dont' currently allow multi width columns..
35499         
35500         var colGroup = this._getColGroup( colSpan );
35501         // get the minimum Y value from the columns
35502         var minimumY = Math.min.apply( Math, colGroup );
35503         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35504         
35505         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35506          
35507         // position the brick
35508         var position = {
35509             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35510             y: this.currentSize.y + minimumY + this.padHeight
35511         };
35512         
35513         Roo.log(position);
35514         // apply setHeight to necessary columns
35515         var setHeight = minimumY + sz.height + this.padHeight;
35516         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35517         
35518         var setSpan = this.cols + 1 - colGroup.length;
35519         for ( var i = 0; i < setSpan; i++ ) {
35520           this.colYs[ shortColIndex + i ] = setHeight ;
35521         }
35522       
35523         return position;
35524     },
35525     
35526     /**
35527      * @param {Number} colSpan - number of columns the element spans
35528      * @returns {Array} colGroup
35529      */
35530     _getColGroup : function( colSpan )
35531     {
35532         if ( colSpan < 2 ) {
35533           // if brick spans only one column, use all the column Ys
35534           return this.colYs;
35535         }
35536       
35537         var colGroup = [];
35538         // how many different places could this brick fit horizontally
35539         var groupCount = this.cols + 1 - colSpan;
35540         // for each group potential horizontal position
35541         for ( var i = 0; i < groupCount; i++ ) {
35542           // make an array of colY values for that one group
35543           var groupColYs = this.colYs.slice( i, i + colSpan );
35544           // and get the max value of the array
35545           colGroup[i] = Math.max.apply( Math, groupColYs );
35546         }
35547         return colGroup;
35548     },
35549     /*
35550     _manageStamp : function( stamp )
35551     {
35552         var stampSize =  stamp.getSize();
35553         var offset = stamp.getBox();
35554         // get the columns that this stamp affects
35555         var firstX = this.isOriginLeft ? offset.x : offset.right;
35556         var lastX = firstX + stampSize.width;
35557         var firstCol = Math.floor( firstX / this.columnWidth );
35558         firstCol = Math.max( 0, firstCol );
35559         
35560         var lastCol = Math.floor( lastX / this.columnWidth );
35561         // lastCol should not go over if multiple of columnWidth #425
35562         lastCol -= lastX % this.columnWidth ? 0 : 1;
35563         lastCol = Math.min( this.cols - 1, lastCol );
35564         
35565         // set colYs to bottom of the stamp
35566         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35567             stampSize.height;
35568             
35569         for ( var i = firstCol; i <= lastCol; i++ ) {
35570           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35571         }
35572     },
35573     */
35574     
35575     _getContainerSize : function()
35576     {
35577         this.maxY = Math.max.apply( Math, this.colYs );
35578         var size = {
35579             height: this.maxY
35580         };
35581       
35582         if ( this.isFitWidth ) {
35583             size.width = this._getContainerFitWidth();
35584         }
35585       
35586         return size;
35587     },
35588     
35589     _getContainerFitWidth : function()
35590     {
35591         var unusedCols = 0;
35592         // count unused columns
35593         var i = this.cols;
35594         while ( --i ) {
35595           if ( this.colYs[i] !== 0 ) {
35596             break;
35597           }
35598           unusedCols++;
35599         }
35600         // fit container to columns that have been used
35601         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35602     },
35603     
35604     needsResizeLayout : function()
35605     {
35606         var previousWidth = this.containerWidth;
35607         this.getContainerWidth();
35608         return previousWidth !== this.containerWidth;
35609     }
35610  
35611 });
35612
35613  
35614
35615  /*
35616  * - LGPL
35617  *
35618  * element
35619  * 
35620  */
35621
35622 /**
35623  * @class Roo.bootstrap.MasonryBrick
35624  * @extends Roo.bootstrap.Component
35625  * Bootstrap MasonryBrick class
35626  * 
35627  * @constructor
35628  * Create a new MasonryBrick
35629  * @param {Object} config The config object
35630  */
35631
35632 Roo.bootstrap.MasonryBrick = function(config){
35633     
35634     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35635     
35636     Roo.bootstrap.MasonryBrick.register(this);
35637     
35638     this.addEvents({
35639         // raw events
35640         /**
35641          * @event click
35642          * When a MasonryBrick is clcik
35643          * @param {Roo.bootstrap.MasonryBrick} this
35644          * @param {Roo.EventObject} e
35645          */
35646         "click" : true
35647     });
35648 };
35649
35650 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35651     
35652     /**
35653      * @cfg {String} title
35654      */   
35655     title : '',
35656     /**
35657      * @cfg {String} html
35658      */   
35659     html : '',
35660     /**
35661      * @cfg {String} bgimage
35662      */   
35663     bgimage : '',
35664     /**
35665      * @cfg {String} videourl
35666      */   
35667     videourl : '',
35668     /**
35669      * @cfg {String} cls
35670      */   
35671     cls : '',
35672     /**
35673      * @cfg {String} href
35674      */   
35675     href : '',
35676     /**
35677      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35678      */   
35679     size : 'xs',
35680     
35681     /**
35682      * @cfg {String} placetitle (center|bottom)
35683      */   
35684     placetitle : '',
35685     
35686     /**
35687      * @cfg {Boolean} isFitContainer defalut true
35688      */   
35689     isFitContainer : true, 
35690     
35691     /**
35692      * @cfg {Boolean} preventDefault defalut false
35693      */   
35694     preventDefault : false, 
35695     
35696     /**
35697      * @cfg {Boolean} inverse defalut false
35698      */   
35699     maskInverse : false, 
35700     
35701     getAutoCreate : function()
35702     {
35703         if(!this.isFitContainer){
35704             return this.getSplitAutoCreate();
35705         }
35706         
35707         var cls = 'masonry-brick masonry-brick-full';
35708         
35709         if(this.href.length){
35710             cls += ' masonry-brick-link';
35711         }
35712         
35713         if(this.bgimage.length){
35714             cls += ' masonry-brick-image';
35715         }
35716         
35717         if(this.maskInverse){
35718             cls += ' mask-inverse';
35719         }
35720         
35721         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35722             cls += ' enable-mask';
35723         }
35724         
35725         if(this.size){
35726             cls += ' masonry-' + this.size + '-brick';
35727         }
35728         
35729         if(this.placetitle.length){
35730             
35731             switch (this.placetitle) {
35732                 case 'center' :
35733                     cls += ' masonry-center-title';
35734                     break;
35735                 case 'bottom' :
35736                     cls += ' masonry-bottom-title';
35737                     break;
35738                 default:
35739                     break;
35740             }
35741             
35742         } else {
35743             if(!this.html.length && !this.bgimage.length){
35744                 cls += ' masonry-center-title';
35745             }
35746
35747             if(!this.html.length && this.bgimage.length){
35748                 cls += ' masonry-bottom-title';
35749             }
35750         }
35751         
35752         if(this.cls){
35753             cls += ' ' + this.cls;
35754         }
35755         
35756         var cfg = {
35757             tag: (this.href.length) ? 'a' : 'div',
35758             cls: cls,
35759             cn: [
35760                 {
35761                     tag: 'div',
35762                     cls: 'masonry-brick-mask'
35763                 },
35764                 {
35765                     tag: 'div',
35766                     cls: 'masonry-brick-paragraph',
35767                     cn: []
35768                 }
35769             ]
35770         };
35771         
35772         if(this.href.length){
35773             cfg.href = this.href;
35774         }
35775         
35776         var cn = cfg.cn[1].cn;
35777         
35778         if(this.title.length){
35779             cn.push({
35780                 tag: 'h4',
35781                 cls: 'masonry-brick-title',
35782                 html: this.title
35783             });
35784         }
35785         
35786         if(this.html.length){
35787             cn.push({
35788                 tag: 'p',
35789                 cls: 'masonry-brick-text',
35790                 html: this.html
35791             });
35792         }
35793         
35794         if (!this.title.length && !this.html.length) {
35795             cfg.cn[1].cls += ' hide';
35796         }
35797         
35798         if(this.bgimage.length){
35799             cfg.cn.push({
35800                 tag: 'img',
35801                 cls: 'masonry-brick-image-view',
35802                 src: this.bgimage
35803             });
35804         }
35805         
35806         if(this.videourl.length){
35807             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35808             // youtube support only?
35809             cfg.cn.push({
35810                 tag: 'iframe',
35811                 cls: 'masonry-brick-image-view',
35812                 src: vurl,
35813                 frameborder : 0,
35814                 allowfullscreen : true
35815             });
35816         }
35817         
35818         return cfg;
35819         
35820     },
35821     
35822     getSplitAutoCreate : function()
35823     {
35824         var cls = 'masonry-brick masonry-brick-split';
35825         
35826         if(this.href.length){
35827             cls += ' masonry-brick-link';
35828         }
35829         
35830         if(this.bgimage.length){
35831             cls += ' masonry-brick-image';
35832         }
35833         
35834         if(this.size){
35835             cls += ' masonry-' + this.size + '-brick';
35836         }
35837         
35838         switch (this.placetitle) {
35839             case 'center' :
35840                 cls += ' masonry-center-title';
35841                 break;
35842             case 'bottom' :
35843                 cls += ' masonry-bottom-title';
35844                 break;
35845             default:
35846                 if(!this.bgimage.length){
35847                     cls += ' masonry-center-title';
35848                 }
35849
35850                 if(this.bgimage.length){
35851                     cls += ' masonry-bottom-title';
35852                 }
35853                 break;
35854         }
35855         
35856         if(this.cls){
35857             cls += ' ' + this.cls;
35858         }
35859         
35860         var cfg = {
35861             tag: (this.href.length) ? 'a' : 'div',
35862             cls: cls,
35863             cn: [
35864                 {
35865                     tag: 'div',
35866                     cls: 'masonry-brick-split-head',
35867                     cn: [
35868                         {
35869                             tag: 'div',
35870                             cls: 'masonry-brick-paragraph',
35871                             cn: []
35872                         }
35873                     ]
35874                 },
35875                 {
35876                     tag: 'div',
35877                     cls: 'masonry-brick-split-body',
35878                     cn: []
35879                 }
35880             ]
35881         };
35882         
35883         if(this.href.length){
35884             cfg.href = this.href;
35885         }
35886         
35887         if(this.title.length){
35888             cfg.cn[0].cn[0].cn.push({
35889                 tag: 'h4',
35890                 cls: 'masonry-brick-title',
35891                 html: this.title
35892             });
35893         }
35894         
35895         if(this.html.length){
35896             cfg.cn[1].cn.push({
35897                 tag: 'p',
35898                 cls: 'masonry-brick-text',
35899                 html: this.html
35900             });
35901         }
35902
35903         if(this.bgimage.length){
35904             cfg.cn[0].cn.push({
35905                 tag: 'img',
35906                 cls: 'masonry-brick-image-view',
35907                 src: this.bgimage
35908             });
35909         }
35910         
35911         if(this.videourl.length){
35912             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35913             // youtube support only?
35914             cfg.cn[0].cn.cn.push({
35915                 tag: 'iframe',
35916                 cls: 'masonry-brick-image-view',
35917                 src: vurl,
35918                 frameborder : 0,
35919                 allowfullscreen : true
35920             });
35921         }
35922         
35923         return cfg;
35924     },
35925     
35926     initEvents: function() 
35927     {
35928         switch (this.size) {
35929             case 'xs' :
35930                 this.x = 1;
35931                 this.y = 1;
35932                 break;
35933             case 'sm' :
35934                 this.x = 2;
35935                 this.y = 2;
35936                 break;
35937             case 'md' :
35938             case 'md-left' :
35939             case 'md-right' :
35940                 this.x = 3;
35941                 this.y = 3;
35942                 break;
35943             case 'tall' :
35944                 this.x = 2;
35945                 this.y = 3;
35946                 break;
35947             case 'wide' :
35948                 this.x = 3;
35949                 this.y = 2;
35950                 break;
35951             case 'wide-thin' :
35952                 this.x = 3;
35953                 this.y = 1;
35954                 break;
35955                         
35956             default :
35957                 break;
35958         }
35959         
35960         if(Roo.isTouch){
35961             this.el.on('touchstart', this.onTouchStart, this);
35962             this.el.on('touchmove', this.onTouchMove, this);
35963             this.el.on('touchend', this.onTouchEnd, this);
35964             this.el.on('contextmenu', this.onContextMenu, this);
35965         } else {
35966             this.el.on('mouseenter'  ,this.enter, this);
35967             this.el.on('mouseleave', this.leave, this);
35968             this.el.on('click', this.onClick, this);
35969         }
35970         
35971         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35972             this.parent().bricks.push(this);   
35973         }
35974         
35975     },
35976     
35977     onClick: function(e, el)
35978     {
35979         var time = this.endTimer - this.startTimer;
35980         // Roo.log(e.preventDefault());
35981         if(Roo.isTouch){
35982             if(time > 1000){
35983                 e.preventDefault();
35984                 return;
35985             }
35986         }
35987         
35988         if(!this.preventDefault){
35989             return;
35990         }
35991         
35992         e.preventDefault();
35993         
35994         if (this.activeClass != '') {
35995             this.selectBrick();
35996         }
35997         
35998         this.fireEvent('click', this, e);
35999     },
36000     
36001     enter: function(e, el)
36002     {
36003         e.preventDefault();
36004         
36005         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36006             return;
36007         }
36008         
36009         if(this.bgimage.length && this.html.length){
36010             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36011         }
36012     },
36013     
36014     leave: function(e, el)
36015     {
36016         e.preventDefault();
36017         
36018         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36019             return;
36020         }
36021         
36022         if(this.bgimage.length && this.html.length){
36023             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36024         }
36025     },
36026     
36027     onTouchStart: function(e, el)
36028     {
36029 //        e.preventDefault();
36030         
36031         this.touchmoved = false;
36032         
36033         if(!this.isFitContainer){
36034             return;
36035         }
36036         
36037         if(!this.bgimage.length || !this.html.length){
36038             return;
36039         }
36040         
36041         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36042         
36043         this.timer = new Date().getTime();
36044         
36045     },
36046     
36047     onTouchMove: function(e, el)
36048     {
36049         this.touchmoved = true;
36050     },
36051     
36052     onContextMenu : function(e,el)
36053     {
36054         e.preventDefault();
36055         e.stopPropagation();
36056         return false;
36057     },
36058     
36059     onTouchEnd: function(e, el)
36060     {
36061 //        e.preventDefault();
36062         
36063         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36064         
36065             this.leave(e,el);
36066             
36067             return;
36068         }
36069         
36070         if(!this.bgimage.length || !this.html.length){
36071             
36072             if(this.href.length){
36073                 window.location.href = this.href;
36074             }
36075             
36076             return;
36077         }
36078         
36079         if(!this.isFitContainer){
36080             return;
36081         }
36082         
36083         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36084         
36085         window.location.href = this.href;
36086     },
36087     
36088     //selection on single brick only
36089     selectBrick : function() {
36090         
36091         if (!this.parentId) {
36092             return;
36093         }
36094         
36095         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36096         var index = m.selectedBrick.indexOf(this.id);
36097         
36098         if ( index > -1) {
36099             m.selectedBrick.splice(index,1);
36100             this.el.removeClass(this.activeClass);
36101             return;
36102         }
36103         
36104         for(var i = 0; i < m.selectedBrick.length; i++) {
36105             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36106             b.el.removeClass(b.activeClass);
36107         }
36108         
36109         m.selectedBrick = [];
36110         
36111         m.selectedBrick.push(this.id);
36112         this.el.addClass(this.activeClass);
36113         return;
36114     },
36115     
36116     isSelected : function(){
36117         return this.el.hasClass(this.activeClass);
36118         
36119     }
36120 });
36121
36122 Roo.apply(Roo.bootstrap.MasonryBrick, {
36123     
36124     //groups: {},
36125     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36126      /**
36127     * register a Masonry Brick
36128     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36129     */
36130     
36131     register : function(brick)
36132     {
36133         //this.groups[brick.id] = brick;
36134         this.groups.add(brick.id, brick);
36135     },
36136     /**
36137     * fetch a  masonry brick based on the masonry brick ID
36138     * @param {string} the masonry brick to add
36139     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36140     */
36141     
36142     get: function(brick_id) 
36143     {
36144         // if (typeof(this.groups[brick_id]) == 'undefined') {
36145         //     return false;
36146         // }
36147         // return this.groups[brick_id] ;
36148         
36149         if(this.groups.key(brick_id)) {
36150             return this.groups.key(brick_id);
36151         }
36152         
36153         return false;
36154     }
36155     
36156     
36157     
36158 });
36159
36160  /*
36161  * - LGPL
36162  *
36163  * element
36164  * 
36165  */
36166
36167 /**
36168  * @class Roo.bootstrap.Brick
36169  * @extends Roo.bootstrap.Component
36170  * Bootstrap Brick class
36171  * 
36172  * @constructor
36173  * Create a new Brick
36174  * @param {Object} config The config object
36175  */
36176
36177 Roo.bootstrap.Brick = function(config){
36178     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36179     
36180     this.addEvents({
36181         // raw events
36182         /**
36183          * @event click
36184          * When a Brick is click
36185          * @param {Roo.bootstrap.Brick} this
36186          * @param {Roo.EventObject} e
36187          */
36188         "click" : true
36189     });
36190 };
36191
36192 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36193     
36194     /**
36195      * @cfg {String} title
36196      */   
36197     title : '',
36198     /**
36199      * @cfg {String} html
36200      */   
36201     html : '',
36202     /**
36203      * @cfg {String} bgimage
36204      */   
36205     bgimage : '',
36206     /**
36207      * @cfg {String} cls
36208      */   
36209     cls : '',
36210     /**
36211      * @cfg {String} href
36212      */   
36213     href : '',
36214     /**
36215      * @cfg {String} video
36216      */   
36217     video : '',
36218     /**
36219      * @cfg {Boolean} square
36220      */   
36221     square : true,
36222     
36223     getAutoCreate : function()
36224     {
36225         var cls = 'roo-brick';
36226         
36227         if(this.href.length){
36228             cls += ' roo-brick-link';
36229         }
36230         
36231         if(this.bgimage.length){
36232             cls += ' roo-brick-image';
36233         }
36234         
36235         if(!this.html.length && !this.bgimage.length){
36236             cls += ' roo-brick-center-title';
36237         }
36238         
36239         if(!this.html.length && this.bgimage.length){
36240             cls += ' roo-brick-bottom-title';
36241         }
36242         
36243         if(this.cls){
36244             cls += ' ' + this.cls;
36245         }
36246         
36247         var cfg = {
36248             tag: (this.href.length) ? 'a' : 'div',
36249             cls: cls,
36250             cn: [
36251                 {
36252                     tag: 'div',
36253                     cls: 'roo-brick-paragraph',
36254                     cn: []
36255                 }
36256             ]
36257         };
36258         
36259         if(this.href.length){
36260             cfg.href = this.href;
36261         }
36262         
36263         var cn = cfg.cn[0].cn;
36264         
36265         if(this.title.length){
36266             cn.push({
36267                 tag: 'h4',
36268                 cls: 'roo-brick-title',
36269                 html: this.title
36270             });
36271         }
36272         
36273         if(this.html.length){
36274             cn.push({
36275                 tag: 'p',
36276                 cls: 'roo-brick-text',
36277                 html: this.html
36278             });
36279         } else {
36280             cn.cls += ' hide';
36281         }
36282         
36283         if(this.bgimage.length){
36284             cfg.cn.push({
36285                 tag: 'img',
36286                 cls: 'roo-brick-image-view',
36287                 src: this.bgimage
36288             });
36289         }
36290         
36291         return cfg;
36292     },
36293     
36294     initEvents: function() 
36295     {
36296         if(this.title.length || this.html.length){
36297             this.el.on('mouseenter'  ,this.enter, this);
36298             this.el.on('mouseleave', this.leave, this);
36299         }
36300         
36301         Roo.EventManager.onWindowResize(this.resize, this); 
36302         
36303         if(this.bgimage.length){
36304             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36305             this.imageEl.on('load', this.onImageLoad, this);
36306             return;
36307         }
36308         
36309         this.resize();
36310     },
36311     
36312     onImageLoad : function()
36313     {
36314         this.resize();
36315     },
36316     
36317     resize : function()
36318     {
36319         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36320         
36321         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36322         
36323         if(this.bgimage.length){
36324             var image = this.el.select('.roo-brick-image-view', true).first();
36325             
36326             image.setWidth(paragraph.getWidth());
36327             
36328             if(this.square){
36329                 image.setHeight(paragraph.getWidth());
36330             }
36331             
36332             this.el.setHeight(image.getHeight());
36333             paragraph.setHeight(image.getHeight());
36334             
36335         }
36336         
36337     },
36338     
36339     enter: function(e, el)
36340     {
36341         e.preventDefault();
36342         
36343         if(this.bgimage.length){
36344             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36345             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36346         }
36347     },
36348     
36349     leave: function(e, el)
36350     {
36351         e.preventDefault();
36352         
36353         if(this.bgimage.length){
36354             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36355             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36356         }
36357     }
36358     
36359 });
36360
36361  
36362
36363  /*
36364  * - LGPL
36365  *
36366  * Number field 
36367  */
36368
36369 /**
36370  * @class Roo.bootstrap.NumberField
36371  * @extends Roo.bootstrap.Input
36372  * Bootstrap NumberField class
36373  * 
36374  * 
36375  * 
36376  * 
36377  * @constructor
36378  * Create a new NumberField
36379  * @param {Object} config The config object
36380  */
36381
36382 Roo.bootstrap.NumberField = function(config){
36383     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36384 };
36385
36386 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36387     
36388     /**
36389      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36390      */
36391     allowDecimals : true,
36392     /**
36393      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36394      */
36395     decimalSeparator : ".",
36396     /**
36397      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36398      */
36399     decimalPrecision : 2,
36400     /**
36401      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36402      */
36403     allowNegative : true,
36404     
36405     /**
36406      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36407      */
36408     allowZero: true,
36409     /**
36410      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36411      */
36412     minValue : Number.NEGATIVE_INFINITY,
36413     /**
36414      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36415      */
36416     maxValue : Number.MAX_VALUE,
36417     /**
36418      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36419      */
36420     minText : "The minimum value for this field is {0}",
36421     /**
36422      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36423      */
36424     maxText : "The maximum value for this field is {0}",
36425     /**
36426      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36427      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36428      */
36429     nanText : "{0} is not a valid number",
36430     /**
36431      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36432      */
36433     thousandsDelimiter : false,
36434     /**
36435      * @cfg {String} valueAlign alignment of value
36436      */
36437     valueAlign : "left",
36438
36439     getAutoCreate : function()
36440     {
36441         var hiddenInput = {
36442             tag: 'input',
36443             type: 'hidden',
36444             id: Roo.id(),
36445             cls: 'hidden-number-input'
36446         };
36447         
36448         if (this.name) {
36449             hiddenInput.name = this.name;
36450         }
36451         
36452         this.name = '';
36453         
36454         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36455         
36456         this.name = hiddenInput.name;
36457         
36458         if(cfg.cn.length > 0) {
36459             cfg.cn.push(hiddenInput);
36460         }
36461         
36462         return cfg;
36463     },
36464
36465     // private
36466     initEvents : function()
36467     {   
36468         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36469         
36470         var allowed = "0123456789";
36471         
36472         if(this.allowDecimals){
36473             allowed += this.decimalSeparator;
36474         }
36475         
36476         if(this.allowNegative){
36477             allowed += "-";
36478         }
36479         
36480         if(this.thousandsDelimiter) {
36481             allowed += ",";
36482         }
36483         
36484         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36485         
36486         var keyPress = function(e){
36487             
36488             var k = e.getKey();
36489             
36490             var c = e.getCharCode();
36491             
36492             if(
36493                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36494                     allowed.indexOf(String.fromCharCode(c)) === -1
36495             ){
36496                 e.stopEvent();
36497                 return;
36498             }
36499             
36500             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36501                 return;
36502             }
36503             
36504             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36505                 e.stopEvent();
36506             }
36507         };
36508         
36509         this.el.on("keypress", keyPress, this);
36510     },
36511     
36512     validateValue : function(value)
36513     {
36514         
36515         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36516             return false;
36517         }
36518         
36519         var num = this.parseValue(value);
36520         
36521         if(isNaN(num)){
36522             this.markInvalid(String.format(this.nanText, value));
36523             return false;
36524         }
36525         
36526         if(num < this.minValue){
36527             this.markInvalid(String.format(this.minText, this.minValue));
36528             return false;
36529         }
36530         
36531         if(num > this.maxValue){
36532             this.markInvalid(String.format(this.maxText, this.maxValue));
36533             return false;
36534         }
36535         
36536         return true;
36537     },
36538
36539     getValue : function()
36540     {
36541         var v = this.hiddenEl().getValue();
36542         
36543         return this.fixPrecision(this.parseValue(v));
36544     },
36545
36546     parseValue : function(value)
36547     {
36548         if(this.thousandsDelimiter) {
36549             value += "";
36550             r = new RegExp(",", "g");
36551             value = value.replace(r, "");
36552         }
36553         
36554         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36555         return isNaN(value) ? '' : value;
36556     },
36557
36558     fixPrecision : function(value)
36559     {
36560         if(this.thousandsDelimiter) {
36561             value += "";
36562             r = new RegExp(",", "g");
36563             value = value.replace(r, "");
36564         }
36565         
36566         var nan = isNaN(value);
36567         
36568         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36569             return nan ? '' : value;
36570         }
36571         return parseFloat(value).toFixed(this.decimalPrecision);
36572     },
36573
36574     setValue : function(v)
36575     {
36576         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36577         
36578         this.value = v;
36579         
36580         if(this.rendered){
36581             
36582             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36583             
36584             this.inputEl().dom.value = (v == '') ? '' :
36585                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36586             
36587             if(!this.allowZero && v === '0') {
36588                 this.hiddenEl().dom.value = '';
36589                 this.inputEl().dom.value = '';
36590             }
36591             
36592             this.validate();
36593         }
36594     },
36595
36596     decimalPrecisionFcn : function(v)
36597     {
36598         return Math.floor(v);
36599     },
36600
36601     beforeBlur : function()
36602     {
36603         var v = this.parseValue(this.getRawValue());
36604         
36605         if(v || v === 0 || v === ''){
36606             this.setValue(v);
36607         }
36608     },
36609     
36610     hiddenEl : function()
36611     {
36612         return this.el.select('input.hidden-number-input',true).first();
36613     }
36614     
36615 });
36616
36617  
36618
36619 /*
36620 * Licence: LGPL
36621 */
36622
36623 /**
36624  * @class Roo.bootstrap.DocumentSlider
36625  * @extends Roo.bootstrap.Component
36626  * Bootstrap DocumentSlider class
36627  * 
36628  * @constructor
36629  * Create a new DocumentViewer
36630  * @param {Object} config The config object
36631  */
36632
36633 Roo.bootstrap.DocumentSlider = function(config){
36634     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36635     
36636     this.files = [];
36637     
36638     this.addEvents({
36639         /**
36640          * @event initial
36641          * Fire after initEvent
36642          * @param {Roo.bootstrap.DocumentSlider} this
36643          */
36644         "initial" : true,
36645         /**
36646          * @event update
36647          * Fire after update
36648          * @param {Roo.bootstrap.DocumentSlider} this
36649          */
36650         "update" : true,
36651         /**
36652          * @event click
36653          * Fire after click
36654          * @param {Roo.bootstrap.DocumentSlider} this
36655          */
36656         "click" : true
36657     });
36658 };
36659
36660 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36661     
36662     files : false,
36663     
36664     indicator : 0,
36665     
36666     getAutoCreate : function()
36667     {
36668         var cfg = {
36669             tag : 'div',
36670             cls : 'roo-document-slider',
36671             cn : [
36672                 {
36673                     tag : 'div',
36674                     cls : 'roo-document-slider-header',
36675                     cn : [
36676                         {
36677                             tag : 'div',
36678                             cls : 'roo-document-slider-header-title'
36679                         }
36680                     ]
36681                 },
36682                 {
36683                     tag : 'div',
36684                     cls : 'roo-document-slider-body',
36685                     cn : [
36686                         {
36687                             tag : 'div',
36688                             cls : 'roo-document-slider-prev',
36689                             cn : [
36690                                 {
36691                                     tag : 'i',
36692                                     cls : 'fa fa-chevron-left'
36693                                 }
36694                             ]
36695                         },
36696                         {
36697                             tag : 'div',
36698                             cls : 'roo-document-slider-thumb',
36699                             cn : [
36700                                 {
36701                                     tag : 'img',
36702                                     cls : 'roo-document-slider-image'
36703                                 }
36704                             ]
36705                         },
36706                         {
36707                             tag : 'div',
36708                             cls : 'roo-document-slider-next',
36709                             cn : [
36710                                 {
36711                                     tag : 'i',
36712                                     cls : 'fa fa-chevron-right'
36713                                 }
36714                             ]
36715                         }
36716                     ]
36717                 }
36718             ]
36719         };
36720         
36721         return cfg;
36722     },
36723     
36724     initEvents : function()
36725     {
36726         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36727         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36728         
36729         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36730         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36731         
36732         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36733         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36734         
36735         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36736         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36737         
36738         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36739         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36740         
36741         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36742         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36743         
36744         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36745         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36746         
36747         this.thumbEl.on('click', this.onClick, this);
36748         
36749         this.prevIndicator.on('click', this.prev, this);
36750         
36751         this.nextIndicator.on('click', this.next, this);
36752         
36753     },
36754     
36755     initial : function()
36756     {
36757         if(this.files.length){
36758             this.indicator = 1;
36759             this.update()
36760         }
36761         
36762         this.fireEvent('initial', this);
36763     },
36764     
36765     update : function()
36766     {
36767         this.imageEl.attr('src', this.files[this.indicator - 1]);
36768         
36769         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36770         
36771         this.prevIndicator.show();
36772         
36773         if(this.indicator == 1){
36774             this.prevIndicator.hide();
36775         }
36776         
36777         this.nextIndicator.show();
36778         
36779         if(this.indicator == this.files.length){
36780             this.nextIndicator.hide();
36781         }
36782         
36783         this.thumbEl.scrollTo('top');
36784         
36785         this.fireEvent('update', this);
36786     },
36787     
36788     onClick : function(e)
36789     {
36790         e.preventDefault();
36791         
36792         this.fireEvent('click', this);
36793     },
36794     
36795     prev : function(e)
36796     {
36797         e.preventDefault();
36798         
36799         this.indicator = Math.max(1, this.indicator - 1);
36800         
36801         this.update();
36802     },
36803     
36804     next : function(e)
36805     {
36806         e.preventDefault();
36807         
36808         this.indicator = Math.min(this.files.length, this.indicator + 1);
36809         
36810         this.update();
36811     }
36812 });
36813 /*
36814  * - LGPL
36815  *
36816  * RadioSet
36817  *
36818  *
36819  */
36820
36821 /**
36822  * @class Roo.bootstrap.RadioSet
36823  * @extends Roo.bootstrap.Input
36824  * Bootstrap RadioSet class
36825  * @cfg {String} indicatorpos (left|right) default left
36826  * @cfg {Boolean} inline (true|false) inline the element (default true)
36827  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36828  * @constructor
36829  * Create a new RadioSet
36830  * @param {Object} config The config object
36831  */
36832
36833 Roo.bootstrap.RadioSet = function(config){
36834     
36835     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36836     
36837     this.radioes = [];
36838     
36839     Roo.bootstrap.RadioSet.register(this);
36840     
36841     this.addEvents({
36842         /**
36843         * @event check
36844         * Fires when the element is checked or unchecked.
36845         * @param {Roo.bootstrap.RadioSet} this This radio
36846         * @param {Roo.bootstrap.Radio} item The checked item
36847         */
36848        check : true,
36849        /**
36850         * @event click
36851         * Fires when the element is click.
36852         * @param {Roo.bootstrap.RadioSet} this This radio set
36853         * @param {Roo.bootstrap.Radio} item The checked item
36854         * @param {Roo.EventObject} e The event object
36855         */
36856        click : true
36857     });
36858     
36859 };
36860
36861 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36862
36863     radioes : false,
36864     
36865     inline : true,
36866     
36867     weight : '',
36868     
36869     indicatorpos : 'left',
36870     
36871     getAutoCreate : function()
36872     {
36873         var label = {
36874             tag : 'label',
36875             cls : 'roo-radio-set-label',
36876             cn : [
36877                 {
36878                     tag : 'span',
36879                     html : this.fieldLabel
36880                 }
36881             ]
36882         };
36883         if (Roo.bootstrap.version == 3) {
36884             
36885             
36886             if(this.indicatorpos == 'left'){
36887                 label.cn.unshift({
36888                     tag : 'i',
36889                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36890                     tooltip : 'This field is required'
36891                 });
36892             } else {
36893                 label.cn.push({
36894                     tag : 'i',
36895                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36896                     tooltip : 'This field is required'
36897                 });
36898             }
36899         }
36900         var items = {
36901             tag : 'div',
36902             cls : 'roo-radio-set-items'
36903         };
36904         
36905         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36906         
36907         if (align === 'left' && this.fieldLabel.length) {
36908             
36909             items = {
36910                 cls : "roo-radio-set-right", 
36911                 cn: [
36912                     items
36913                 ]
36914             };
36915             
36916             if(this.labelWidth > 12){
36917                 label.style = "width: " + this.labelWidth + 'px';
36918             }
36919             
36920             if(this.labelWidth < 13 && this.labelmd == 0){
36921                 this.labelmd = this.labelWidth;
36922             }
36923             
36924             if(this.labellg > 0){
36925                 label.cls += ' col-lg-' + this.labellg;
36926                 items.cls += ' col-lg-' + (12 - this.labellg);
36927             }
36928             
36929             if(this.labelmd > 0){
36930                 label.cls += ' col-md-' + this.labelmd;
36931                 items.cls += ' col-md-' + (12 - this.labelmd);
36932             }
36933             
36934             if(this.labelsm > 0){
36935                 label.cls += ' col-sm-' + this.labelsm;
36936                 items.cls += ' col-sm-' + (12 - this.labelsm);
36937             }
36938             
36939             if(this.labelxs > 0){
36940                 label.cls += ' col-xs-' + this.labelxs;
36941                 items.cls += ' col-xs-' + (12 - this.labelxs);
36942             }
36943         }
36944         
36945         var cfg = {
36946             tag : 'div',
36947             cls : 'roo-radio-set',
36948             cn : [
36949                 {
36950                     tag : 'input',
36951                     cls : 'roo-radio-set-input',
36952                     type : 'hidden',
36953                     name : this.name,
36954                     value : this.value ? this.value :  ''
36955                 },
36956                 label,
36957                 items
36958             ]
36959         };
36960         
36961         if(this.weight.length){
36962             cfg.cls += ' roo-radio-' + this.weight;
36963         }
36964         
36965         if(this.inline) {
36966             cfg.cls += ' roo-radio-set-inline';
36967         }
36968         
36969         var settings=this;
36970         ['xs','sm','md','lg'].map(function(size){
36971             if (settings[size]) {
36972                 cfg.cls += ' col-' + size + '-' + settings[size];
36973             }
36974         });
36975         
36976         return cfg;
36977         
36978     },
36979
36980     initEvents : function()
36981     {
36982         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36983         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36984         
36985         if(!this.fieldLabel.length){
36986             this.labelEl.hide();
36987         }
36988         
36989         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36990         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36991         
36992         this.indicator = this.indicatorEl();
36993         
36994         if(this.indicator){
36995             this.indicator.addClass('invisible');
36996         }
36997         
36998         this.originalValue = this.getValue();
36999         
37000     },
37001     
37002     inputEl: function ()
37003     {
37004         return this.el.select('.roo-radio-set-input', true).first();
37005     },
37006     
37007     getChildContainer : function()
37008     {
37009         return this.itemsEl;
37010     },
37011     
37012     register : function(item)
37013     {
37014         this.radioes.push(item);
37015         
37016     },
37017     
37018     validate : function()
37019     {   
37020         if(this.getVisibilityEl().hasClass('hidden')){
37021             return true;
37022         }
37023         
37024         var valid = false;
37025         
37026         Roo.each(this.radioes, function(i){
37027             if(!i.checked){
37028                 return;
37029             }
37030             
37031             valid = true;
37032             return false;
37033         });
37034         
37035         if(this.allowBlank) {
37036             return true;
37037         }
37038         
37039         if(this.disabled || valid){
37040             this.markValid();
37041             return true;
37042         }
37043         
37044         this.markInvalid();
37045         return false;
37046         
37047     },
37048     
37049     markValid : function()
37050     {
37051         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37052             this.indicatorEl().removeClass('visible');
37053             this.indicatorEl().addClass('invisible');
37054         }
37055         
37056         
37057         if (Roo.bootstrap.version == 3) {
37058             this.el.removeClass([this.invalidClass, this.validClass]);
37059             this.el.addClass(this.validClass);
37060         } else {
37061             this.el.removeClass(['is-invalid','is-valid']);
37062             this.el.addClass(['is-valid']);
37063         }
37064         this.fireEvent('valid', this);
37065     },
37066     
37067     markInvalid : function(msg)
37068     {
37069         if(this.allowBlank || this.disabled){
37070             return;
37071         }
37072         
37073         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37074             this.indicatorEl().removeClass('invisible');
37075             this.indicatorEl().addClass('visible');
37076         }
37077         if (Roo.bootstrap.version == 3) {
37078             this.el.removeClass([this.invalidClass, this.validClass]);
37079             this.el.addClass(this.invalidClass);
37080         } else {
37081             this.el.removeClass(['is-invalid','is-valid']);
37082             this.el.addClass(['is-invalid']);
37083         }
37084         
37085         this.fireEvent('invalid', this, msg);
37086         
37087     },
37088     
37089     setValue : function(v, suppressEvent)
37090     {   
37091         if(this.value === v){
37092             return;
37093         }
37094         
37095         this.value = v;
37096         
37097         if(this.rendered){
37098             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37099         }
37100         
37101         Roo.each(this.radioes, function(i){
37102             i.checked = false;
37103             i.el.removeClass('checked');
37104         });
37105         
37106         Roo.each(this.radioes, function(i){
37107             
37108             if(i.value === v || i.value.toString() === v.toString()){
37109                 i.checked = true;
37110                 i.el.addClass('checked');
37111                 
37112                 if(suppressEvent !== true){
37113                     this.fireEvent('check', this, i);
37114                 }
37115                 
37116                 return false;
37117             }
37118             
37119         }, this);
37120         
37121         this.validate();
37122     },
37123     
37124     clearInvalid : function(){
37125         
37126         if(!this.el || this.preventMark){
37127             return;
37128         }
37129         
37130         this.el.removeClass([this.invalidClass]);
37131         
37132         this.fireEvent('valid', this);
37133     }
37134     
37135 });
37136
37137 Roo.apply(Roo.bootstrap.RadioSet, {
37138     
37139     groups: {},
37140     
37141     register : function(set)
37142     {
37143         this.groups[set.name] = set;
37144     },
37145     
37146     get: function(name) 
37147     {
37148         if (typeof(this.groups[name]) == 'undefined') {
37149             return false;
37150         }
37151         
37152         return this.groups[name] ;
37153     }
37154     
37155 });
37156 /*
37157  * Based on:
37158  * Ext JS Library 1.1.1
37159  * Copyright(c) 2006-2007, Ext JS, LLC.
37160  *
37161  * Originally Released Under LGPL - original licence link has changed is not relivant.
37162  *
37163  * Fork - LGPL
37164  * <script type="text/javascript">
37165  */
37166
37167
37168 /**
37169  * @class Roo.bootstrap.SplitBar
37170  * @extends Roo.util.Observable
37171  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37172  * <br><br>
37173  * Usage:
37174  * <pre><code>
37175 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37176                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37177 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37178 split.minSize = 100;
37179 split.maxSize = 600;
37180 split.animate = true;
37181 split.on('moved', splitterMoved);
37182 </code></pre>
37183  * @constructor
37184  * Create a new SplitBar
37185  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37186  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37187  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37188  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37189                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37190                         position of the SplitBar).
37191  */
37192 Roo.bootstrap.SplitBar = function(cfg){
37193     
37194     /** @private */
37195     
37196     //{
37197     //  dragElement : elm
37198     //  resizingElement: el,
37199         // optional..
37200     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37201     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37202         // existingProxy ???
37203     //}
37204     
37205     this.el = Roo.get(cfg.dragElement, true);
37206     this.el.dom.unselectable = "on";
37207     /** @private */
37208     this.resizingEl = Roo.get(cfg.resizingElement, true);
37209
37210     /**
37211      * @private
37212      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37213      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37214      * @type Number
37215      */
37216     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37217     
37218     /**
37219      * The minimum size of the resizing element. (Defaults to 0)
37220      * @type Number
37221      */
37222     this.minSize = 0;
37223     
37224     /**
37225      * The maximum size of the resizing element. (Defaults to 2000)
37226      * @type Number
37227      */
37228     this.maxSize = 2000;
37229     
37230     /**
37231      * Whether to animate the transition to the new size
37232      * @type Boolean
37233      */
37234     this.animate = false;
37235     
37236     /**
37237      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37238      * @type Boolean
37239      */
37240     this.useShim = false;
37241     
37242     /** @private */
37243     this.shim = null;
37244     
37245     if(!cfg.existingProxy){
37246         /** @private */
37247         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37248     }else{
37249         this.proxy = Roo.get(cfg.existingProxy).dom;
37250     }
37251     /** @private */
37252     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37253     
37254     /** @private */
37255     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37256     
37257     /** @private */
37258     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37259     
37260     /** @private */
37261     this.dragSpecs = {};
37262     
37263     /**
37264      * @private The adapter to use to positon and resize elements
37265      */
37266     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37267     this.adapter.init(this);
37268     
37269     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37270         /** @private */
37271         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37272         this.el.addClass("roo-splitbar-h");
37273     }else{
37274         /** @private */
37275         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37276         this.el.addClass("roo-splitbar-v");
37277     }
37278     
37279     this.addEvents({
37280         /**
37281          * @event resize
37282          * Fires when the splitter is moved (alias for {@link #event-moved})
37283          * @param {Roo.bootstrap.SplitBar} this
37284          * @param {Number} newSize the new width or height
37285          */
37286         "resize" : true,
37287         /**
37288          * @event moved
37289          * Fires when the splitter is moved
37290          * @param {Roo.bootstrap.SplitBar} this
37291          * @param {Number} newSize the new width or height
37292          */
37293         "moved" : true,
37294         /**
37295          * @event beforeresize
37296          * Fires before the splitter is dragged
37297          * @param {Roo.bootstrap.SplitBar} this
37298          */
37299         "beforeresize" : true,
37300
37301         "beforeapply" : true
37302     });
37303
37304     Roo.util.Observable.call(this);
37305 };
37306
37307 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37308     onStartProxyDrag : function(x, y){
37309         this.fireEvent("beforeresize", this);
37310         if(!this.overlay){
37311             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37312             o.unselectable();
37313             o.enableDisplayMode("block");
37314             // all splitbars share the same overlay
37315             Roo.bootstrap.SplitBar.prototype.overlay = o;
37316         }
37317         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37318         this.overlay.show();
37319         Roo.get(this.proxy).setDisplayed("block");
37320         var size = this.adapter.getElementSize(this);
37321         this.activeMinSize = this.getMinimumSize();;
37322         this.activeMaxSize = this.getMaximumSize();;
37323         var c1 = size - this.activeMinSize;
37324         var c2 = Math.max(this.activeMaxSize - size, 0);
37325         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37326             this.dd.resetConstraints();
37327             this.dd.setXConstraint(
37328                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37329                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37330             );
37331             this.dd.setYConstraint(0, 0);
37332         }else{
37333             this.dd.resetConstraints();
37334             this.dd.setXConstraint(0, 0);
37335             this.dd.setYConstraint(
37336                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37337                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37338             );
37339          }
37340         this.dragSpecs.startSize = size;
37341         this.dragSpecs.startPoint = [x, y];
37342         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37343     },
37344     
37345     /** 
37346      * @private Called after the drag operation by the DDProxy
37347      */
37348     onEndProxyDrag : function(e){
37349         Roo.get(this.proxy).setDisplayed(false);
37350         var endPoint = Roo.lib.Event.getXY(e);
37351         if(this.overlay){
37352             this.overlay.hide();
37353         }
37354         var newSize;
37355         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37356             newSize = this.dragSpecs.startSize + 
37357                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37358                     endPoint[0] - this.dragSpecs.startPoint[0] :
37359                     this.dragSpecs.startPoint[0] - endPoint[0]
37360                 );
37361         }else{
37362             newSize = this.dragSpecs.startSize + 
37363                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37364                     endPoint[1] - this.dragSpecs.startPoint[1] :
37365                     this.dragSpecs.startPoint[1] - endPoint[1]
37366                 );
37367         }
37368         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37369         if(newSize != this.dragSpecs.startSize){
37370             if(this.fireEvent('beforeapply', this, newSize) !== false){
37371                 this.adapter.setElementSize(this, newSize);
37372                 this.fireEvent("moved", this, newSize);
37373                 this.fireEvent("resize", this, newSize);
37374             }
37375         }
37376     },
37377     
37378     /**
37379      * Get the adapter this SplitBar uses
37380      * @return The adapter object
37381      */
37382     getAdapter : function(){
37383         return this.adapter;
37384     },
37385     
37386     /**
37387      * Set the adapter this SplitBar uses
37388      * @param {Object} adapter A SplitBar adapter object
37389      */
37390     setAdapter : function(adapter){
37391         this.adapter = adapter;
37392         this.adapter.init(this);
37393     },
37394     
37395     /**
37396      * Gets the minimum size for the resizing element
37397      * @return {Number} The minimum size
37398      */
37399     getMinimumSize : function(){
37400         return this.minSize;
37401     },
37402     
37403     /**
37404      * Sets the minimum size for the resizing element
37405      * @param {Number} minSize The minimum size
37406      */
37407     setMinimumSize : function(minSize){
37408         this.minSize = minSize;
37409     },
37410     
37411     /**
37412      * Gets the maximum size for the resizing element
37413      * @return {Number} The maximum size
37414      */
37415     getMaximumSize : function(){
37416         return this.maxSize;
37417     },
37418     
37419     /**
37420      * Sets the maximum size for the resizing element
37421      * @param {Number} maxSize The maximum size
37422      */
37423     setMaximumSize : function(maxSize){
37424         this.maxSize = maxSize;
37425     },
37426     
37427     /**
37428      * Sets the initialize size for the resizing element
37429      * @param {Number} size The initial size
37430      */
37431     setCurrentSize : function(size){
37432         var oldAnimate = this.animate;
37433         this.animate = false;
37434         this.adapter.setElementSize(this, size);
37435         this.animate = oldAnimate;
37436     },
37437     
37438     /**
37439      * Destroy this splitbar. 
37440      * @param {Boolean} removeEl True to remove the element
37441      */
37442     destroy : function(removeEl){
37443         if(this.shim){
37444             this.shim.remove();
37445         }
37446         this.dd.unreg();
37447         this.proxy.parentNode.removeChild(this.proxy);
37448         if(removeEl){
37449             this.el.remove();
37450         }
37451     }
37452 });
37453
37454 /**
37455  * @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.
37456  */
37457 Roo.bootstrap.SplitBar.createProxy = function(dir){
37458     var proxy = new Roo.Element(document.createElement("div"));
37459     proxy.unselectable();
37460     var cls = 'roo-splitbar-proxy';
37461     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37462     document.body.appendChild(proxy.dom);
37463     return proxy.dom;
37464 };
37465
37466 /** 
37467  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37468  * Default Adapter. It assumes the splitter and resizing element are not positioned
37469  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37470  */
37471 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37472 };
37473
37474 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37475     // do nothing for now
37476     init : function(s){
37477     
37478     },
37479     /**
37480      * Called before drag operations to get the current size of the resizing element. 
37481      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37482      */
37483      getElementSize : function(s){
37484         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37485             return s.resizingEl.getWidth();
37486         }else{
37487             return s.resizingEl.getHeight();
37488         }
37489     },
37490     
37491     /**
37492      * Called after drag operations to set the size of the resizing element.
37493      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37494      * @param {Number} newSize The new size to set
37495      * @param {Function} onComplete A function to be invoked when resizing is complete
37496      */
37497     setElementSize : function(s, newSize, onComplete){
37498         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37499             if(!s.animate){
37500                 s.resizingEl.setWidth(newSize);
37501                 if(onComplete){
37502                     onComplete(s, newSize);
37503                 }
37504             }else{
37505                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37506             }
37507         }else{
37508             
37509             if(!s.animate){
37510                 s.resizingEl.setHeight(newSize);
37511                 if(onComplete){
37512                     onComplete(s, newSize);
37513                 }
37514             }else{
37515                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37516             }
37517         }
37518     }
37519 };
37520
37521 /** 
37522  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37523  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37524  * Adapter that  moves the splitter element to align with the resized sizing element. 
37525  * Used with an absolute positioned SplitBar.
37526  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37527  * document.body, make sure you assign an id to the body element.
37528  */
37529 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37530     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37531     this.container = Roo.get(container);
37532 };
37533
37534 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37535     init : function(s){
37536         this.basic.init(s);
37537     },
37538     
37539     getElementSize : function(s){
37540         return this.basic.getElementSize(s);
37541     },
37542     
37543     setElementSize : function(s, newSize, onComplete){
37544         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37545     },
37546     
37547     moveSplitter : function(s){
37548         var yes = Roo.bootstrap.SplitBar;
37549         switch(s.placement){
37550             case yes.LEFT:
37551                 s.el.setX(s.resizingEl.getRight());
37552                 break;
37553             case yes.RIGHT:
37554                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37555                 break;
37556             case yes.TOP:
37557                 s.el.setY(s.resizingEl.getBottom());
37558                 break;
37559             case yes.BOTTOM:
37560                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37561                 break;
37562         }
37563     }
37564 };
37565
37566 /**
37567  * Orientation constant - Create a vertical SplitBar
37568  * @static
37569  * @type Number
37570  */
37571 Roo.bootstrap.SplitBar.VERTICAL = 1;
37572
37573 /**
37574  * Orientation constant - Create a horizontal SplitBar
37575  * @static
37576  * @type Number
37577  */
37578 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37579
37580 /**
37581  * Placement constant - The resizing element is to the left of the splitter element
37582  * @static
37583  * @type Number
37584  */
37585 Roo.bootstrap.SplitBar.LEFT = 1;
37586
37587 /**
37588  * Placement constant - The resizing element is to the right of the splitter element
37589  * @static
37590  * @type Number
37591  */
37592 Roo.bootstrap.SplitBar.RIGHT = 2;
37593
37594 /**
37595  * Placement constant - The resizing element is positioned above the splitter element
37596  * @static
37597  * @type Number
37598  */
37599 Roo.bootstrap.SplitBar.TOP = 3;
37600
37601 /**
37602  * Placement constant - The resizing element is positioned under splitter element
37603  * @static
37604  * @type Number
37605  */
37606 Roo.bootstrap.SplitBar.BOTTOM = 4;
37607 Roo.namespace("Roo.bootstrap.layout");/*
37608  * Based on:
37609  * Ext JS Library 1.1.1
37610  * Copyright(c) 2006-2007, Ext JS, LLC.
37611  *
37612  * Originally Released Under LGPL - original licence link has changed is not relivant.
37613  *
37614  * Fork - LGPL
37615  * <script type="text/javascript">
37616  */
37617
37618 /**
37619  * @class Roo.bootstrap.layout.Manager
37620  * @extends Roo.bootstrap.Component
37621  * Base class for layout managers.
37622  */
37623 Roo.bootstrap.layout.Manager = function(config)
37624 {
37625     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37626
37627
37628
37629
37630
37631     /** false to disable window resize monitoring @type Boolean */
37632     this.monitorWindowResize = true;
37633     this.regions = {};
37634     this.addEvents({
37635         /**
37636          * @event layout
37637          * Fires when a layout is performed.
37638          * @param {Roo.LayoutManager} this
37639          */
37640         "layout" : true,
37641         /**
37642          * @event regionresized
37643          * Fires when the user resizes a region.
37644          * @param {Roo.LayoutRegion} region The resized region
37645          * @param {Number} newSize The new size (width for east/west, height for north/south)
37646          */
37647         "regionresized" : true,
37648         /**
37649          * @event regioncollapsed
37650          * Fires when a region is collapsed.
37651          * @param {Roo.LayoutRegion} region The collapsed region
37652          */
37653         "regioncollapsed" : true,
37654         /**
37655          * @event regionexpanded
37656          * Fires when a region is expanded.
37657          * @param {Roo.LayoutRegion} region The expanded region
37658          */
37659         "regionexpanded" : true
37660     });
37661     this.updating = false;
37662
37663     if (config.el) {
37664         this.el = Roo.get(config.el);
37665         this.initEvents();
37666     }
37667
37668 };
37669
37670 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37671
37672
37673     regions : null,
37674
37675     monitorWindowResize : true,
37676
37677
37678     updating : false,
37679
37680
37681     onRender : function(ct, position)
37682     {
37683         if(!this.el){
37684             this.el = Roo.get(ct);
37685             this.initEvents();
37686         }
37687         //this.fireEvent('render',this);
37688     },
37689
37690
37691     initEvents: function()
37692     {
37693
37694
37695         // ie scrollbar fix
37696         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37697             document.body.scroll = "no";
37698         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37699             this.el.position('relative');
37700         }
37701         this.id = this.el.id;
37702         this.el.addClass("roo-layout-container");
37703         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37704         if(this.el.dom != document.body ) {
37705             this.el.on('resize', this.layout,this);
37706             this.el.on('show', this.layout,this);
37707         }
37708
37709     },
37710
37711     /**
37712      * Returns true if this layout is currently being updated
37713      * @return {Boolean}
37714      */
37715     isUpdating : function(){
37716         return this.updating;
37717     },
37718
37719     /**
37720      * Suspend the LayoutManager from doing auto-layouts while
37721      * making multiple add or remove calls
37722      */
37723     beginUpdate : function(){
37724         this.updating = true;
37725     },
37726
37727     /**
37728      * Restore auto-layouts and optionally disable the manager from performing a layout
37729      * @param {Boolean} noLayout true to disable a layout update
37730      */
37731     endUpdate : function(noLayout){
37732         this.updating = false;
37733         if(!noLayout){
37734             this.layout();
37735         }
37736     },
37737
37738     layout: function(){
37739         // abstract...
37740     },
37741
37742     onRegionResized : function(region, newSize){
37743         this.fireEvent("regionresized", region, newSize);
37744         this.layout();
37745     },
37746
37747     onRegionCollapsed : function(region){
37748         this.fireEvent("regioncollapsed", region);
37749     },
37750
37751     onRegionExpanded : function(region){
37752         this.fireEvent("regionexpanded", region);
37753     },
37754
37755     /**
37756      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37757      * performs box-model adjustments.
37758      * @return {Object} The size as an object {width: (the width), height: (the height)}
37759      */
37760     getViewSize : function()
37761     {
37762         var size;
37763         if(this.el.dom != document.body){
37764             size = this.el.getSize();
37765         }else{
37766             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37767         }
37768         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37769         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37770         return size;
37771     },
37772
37773     /**
37774      * Returns the Element this layout is bound to.
37775      * @return {Roo.Element}
37776      */
37777     getEl : function(){
37778         return this.el;
37779     },
37780
37781     /**
37782      * Returns the specified region.
37783      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37784      * @return {Roo.LayoutRegion}
37785      */
37786     getRegion : function(target){
37787         return this.regions[target.toLowerCase()];
37788     },
37789
37790     onWindowResize : function(){
37791         if(this.monitorWindowResize){
37792             this.layout();
37793         }
37794     }
37795 });
37796 /*
37797  * Based on:
37798  * Ext JS Library 1.1.1
37799  * Copyright(c) 2006-2007, Ext JS, LLC.
37800  *
37801  * Originally Released Under LGPL - original licence link has changed is not relivant.
37802  *
37803  * Fork - LGPL
37804  * <script type="text/javascript">
37805  */
37806 /**
37807  * @class Roo.bootstrap.layout.Border
37808  * @extends Roo.bootstrap.layout.Manager
37809  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37810  * please see: examples/bootstrap/nested.html<br><br>
37811  
37812 <b>The container the layout is rendered into can be either the body element or any other element.
37813 If it is not the body element, the container needs to either be an absolute positioned element,
37814 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37815 the container size if it is not the body element.</b>
37816
37817 * @constructor
37818 * Create a new Border
37819 * @param {Object} config Configuration options
37820  */
37821 Roo.bootstrap.layout.Border = function(config){
37822     config = config || {};
37823     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37824     
37825     
37826     
37827     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37828         if(config[region]){
37829             config[region].region = region;
37830             this.addRegion(config[region]);
37831         }
37832     },this);
37833     
37834 };
37835
37836 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37837
37838 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37839     
37840     parent : false, // this might point to a 'nest' or a ???
37841     
37842     /**
37843      * Creates and adds a new region if it doesn't already exist.
37844      * @param {String} target The target region key (north, south, east, west or center).
37845      * @param {Object} config The regions config object
37846      * @return {BorderLayoutRegion} The new region
37847      */
37848     addRegion : function(config)
37849     {
37850         if(!this.regions[config.region]){
37851             var r = this.factory(config);
37852             this.bindRegion(r);
37853         }
37854         return this.regions[config.region];
37855     },
37856
37857     // private (kinda)
37858     bindRegion : function(r){
37859         this.regions[r.config.region] = r;
37860         
37861         r.on("visibilitychange",    this.layout, this);
37862         r.on("paneladded",          this.layout, this);
37863         r.on("panelremoved",        this.layout, this);
37864         r.on("invalidated",         this.layout, this);
37865         r.on("resized",             this.onRegionResized, this);
37866         r.on("collapsed",           this.onRegionCollapsed, this);
37867         r.on("expanded",            this.onRegionExpanded, this);
37868     },
37869
37870     /**
37871      * Performs a layout update.
37872      */
37873     layout : function()
37874     {
37875         if(this.updating) {
37876             return;
37877         }
37878         
37879         // render all the rebions if they have not been done alreayd?
37880         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37881             if(this.regions[region] && !this.regions[region].bodyEl){
37882                 this.regions[region].onRender(this.el)
37883             }
37884         },this);
37885         
37886         var size = this.getViewSize();
37887         var w = size.width;
37888         var h = size.height;
37889         var centerW = w;
37890         var centerH = h;
37891         var centerY = 0;
37892         var centerX = 0;
37893         //var x = 0, y = 0;
37894
37895         var rs = this.regions;
37896         var north = rs["north"];
37897         var south = rs["south"]; 
37898         var west = rs["west"];
37899         var east = rs["east"];
37900         var center = rs["center"];
37901         //if(this.hideOnLayout){ // not supported anymore
37902             //c.el.setStyle("display", "none");
37903         //}
37904         if(north && north.isVisible()){
37905             var b = north.getBox();
37906             var m = north.getMargins();
37907             b.width = w - (m.left+m.right);
37908             b.x = m.left;
37909             b.y = m.top;
37910             centerY = b.height + b.y + m.bottom;
37911             centerH -= centerY;
37912             north.updateBox(this.safeBox(b));
37913         }
37914         if(south && south.isVisible()){
37915             var b = south.getBox();
37916             var m = south.getMargins();
37917             b.width = w - (m.left+m.right);
37918             b.x = m.left;
37919             var totalHeight = (b.height + m.top + m.bottom);
37920             b.y = h - totalHeight + m.top;
37921             centerH -= totalHeight;
37922             south.updateBox(this.safeBox(b));
37923         }
37924         if(west && west.isVisible()){
37925             var b = west.getBox();
37926             var m = west.getMargins();
37927             b.height = centerH - (m.top+m.bottom);
37928             b.x = m.left;
37929             b.y = centerY + m.top;
37930             var totalWidth = (b.width + m.left + m.right);
37931             centerX += totalWidth;
37932             centerW -= totalWidth;
37933             west.updateBox(this.safeBox(b));
37934         }
37935         if(east && east.isVisible()){
37936             var b = east.getBox();
37937             var m = east.getMargins();
37938             b.height = centerH - (m.top+m.bottom);
37939             var totalWidth = (b.width + m.left + m.right);
37940             b.x = w - totalWidth + m.left;
37941             b.y = centerY + m.top;
37942             centerW -= totalWidth;
37943             east.updateBox(this.safeBox(b));
37944         }
37945         if(center){
37946             var m = center.getMargins();
37947             var centerBox = {
37948                 x: centerX + m.left,
37949                 y: centerY + m.top,
37950                 width: centerW - (m.left+m.right),
37951                 height: centerH - (m.top+m.bottom)
37952             };
37953             //if(this.hideOnLayout){
37954                 //center.el.setStyle("display", "block");
37955             //}
37956             center.updateBox(this.safeBox(centerBox));
37957         }
37958         this.el.repaint();
37959         this.fireEvent("layout", this);
37960     },
37961
37962     // private
37963     safeBox : function(box){
37964         box.width = Math.max(0, box.width);
37965         box.height = Math.max(0, box.height);
37966         return box;
37967     },
37968
37969     /**
37970      * Adds a ContentPanel (or subclass) to this layout.
37971      * @param {String} target The target region key (north, south, east, west or center).
37972      * @param {Roo.ContentPanel} panel The panel to add
37973      * @return {Roo.ContentPanel} The added panel
37974      */
37975     add : function(target, panel){
37976          
37977         target = target.toLowerCase();
37978         return this.regions[target].add(panel);
37979     },
37980
37981     /**
37982      * Remove a ContentPanel (or subclass) to this layout.
37983      * @param {String} target The target region key (north, south, east, west or center).
37984      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37985      * @return {Roo.ContentPanel} The removed panel
37986      */
37987     remove : function(target, panel){
37988         target = target.toLowerCase();
37989         return this.regions[target].remove(panel);
37990     },
37991
37992     /**
37993      * Searches all regions for a panel with the specified id
37994      * @param {String} panelId
37995      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37996      */
37997     findPanel : function(panelId){
37998         var rs = this.regions;
37999         for(var target in rs){
38000             if(typeof rs[target] != "function"){
38001                 var p = rs[target].getPanel(panelId);
38002                 if(p){
38003                     return p;
38004                 }
38005             }
38006         }
38007         return null;
38008     },
38009
38010     /**
38011      * Searches all regions for a panel with the specified id and activates (shows) it.
38012      * @param {String/ContentPanel} panelId The panels id or the panel itself
38013      * @return {Roo.ContentPanel} The shown panel or null
38014      */
38015     showPanel : function(panelId) {
38016       var rs = this.regions;
38017       for(var target in rs){
38018          var r = rs[target];
38019          if(typeof r != "function"){
38020             if(r.hasPanel(panelId)){
38021                return r.showPanel(panelId);
38022             }
38023          }
38024       }
38025       return null;
38026    },
38027
38028    /**
38029      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38030      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38031      */
38032    /*
38033     restoreState : function(provider){
38034         if(!provider){
38035             provider = Roo.state.Manager;
38036         }
38037         var sm = new Roo.LayoutStateManager();
38038         sm.init(this, provider);
38039     },
38040 */
38041  
38042  
38043     /**
38044      * Adds a xtype elements to the layout.
38045      * <pre><code>
38046
38047 layout.addxtype({
38048        xtype : 'ContentPanel',
38049        region: 'west',
38050        items: [ .... ]
38051    }
38052 );
38053
38054 layout.addxtype({
38055         xtype : 'NestedLayoutPanel',
38056         region: 'west',
38057         layout: {
38058            center: { },
38059            west: { }   
38060         },
38061         items : [ ... list of content panels or nested layout panels.. ]
38062    }
38063 );
38064 </code></pre>
38065      * @param {Object} cfg Xtype definition of item to add.
38066      */
38067     addxtype : function(cfg)
38068     {
38069         // basically accepts a pannel...
38070         // can accept a layout region..!?!?
38071         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38072         
38073         
38074         // theory?  children can only be panels??
38075         
38076         //if (!cfg.xtype.match(/Panel$/)) {
38077         //    return false;
38078         //}
38079         var ret = false;
38080         
38081         if (typeof(cfg.region) == 'undefined') {
38082             Roo.log("Failed to add Panel, region was not set");
38083             Roo.log(cfg);
38084             return false;
38085         }
38086         var region = cfg.region;
38087         delete cfg.region;
38088         
38089           
38090         var xitems = [];
38091         if (cfg.items) {
38092             xitems = cfg.items;
38093             delete cfg.items;
38094         }
38095         var nb = false;
38096         
38097         if ( region == 'center') {
38098             Roo.log("Center: " + cfg.title);
38099         }
38100         
38101         
38102         switch(cfg.xtype) 
38103         {
38104             case 'Content':  // ContentPanel (el, cfg)
38105             case 'Scroll':  // ContentPanel (el, cfg)
38106             case 'View': 
38107                 cfg.autoCreate = cfg.autoCreate || true;
38108                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38109                 //} else {
38110                 //    var el = this.el.createChild();
38111                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38112                 //}
38113                 
38114                 this.add(region, ret);
38115                 break;
38116             
38117             /*
38118             case 'TreePanel': // our new panel!
38119                 cfg.el = this.el.createChild();
38120                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38121                 this.add(region, ret);
38122                 break;
38123             */
38124             
38125             case 'Nest': 
38126                 // create a new Layout (which is  a Border Layout...
38127                 
38128                 var clayout = cfg.layout;
38129                 clayout.el  = this.el.createChild();
38130                 clayout.items   = clayout.items  || [];
38131                 
38132                 delete cfg.layout;
38133                 
38134                 // replace this exitems with the clayout ones..
38135                 xitems = clayout.items;
38136                  
38137                 // force background off if it's in center...
38138                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38139                     cfg.background = false;
38140                 }
38141                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38142                 
38143                 
38144                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38145                 //console.log('adding nested layout panel '  + cfg.toSource());
38146                 this.add(region, ret);
38147                 nb = {}; /// find first...
38148                 break;
38149             
38150             case 'Grid':
38151                 
38152                 // needs grid and region
38153                 
38154                 //var el = this.getRegion(region).el.createChild();
38155                 /*
38156                  *var el = this.el.createChild();
38157                 // create the grid first...
38158                 cfg.grid.container = el;
38159                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38160                 */
38161                 
38162                 if (region == 'center' && this.active ) {
38163                     cfg.background = false;
38164                 }
38165                 
38166                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38167                 
38168                 this.add(region, ret);
38169                 /*
38170                 if (cfg.background) {
38171                     // render grid on panel activation (if panel background)
38172                     ret.on('activate', function(gp) {
38173                         if (!gp.grid.rendered) {
38174                     //        gp.grid.render(el);
38175                         }
38176                     });
38177                 } else {
38178                   //  cfg.grid.render(el);
38179                 }
38180                 */
38181                 break;
38182            
38183            
38184             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38185                 // it was the old xcomponent building that caused this before.
38186                 // espeically if border is the top element in the tree.
38187                 ret = this;
38188                 break; 
38189                 
38190                     
38191                 
38192                 
38193                 
38194             default:
38195                 /*
38196                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38197                     
38198                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38199                     this.add(region, ret);
38200                 } else {
38201                 */
38202                     Roo.log(cfg);
38203                     throw "Can not add '" + cfg.xtype + "' to Border";
38204                     return null;
38205              
38206                                 
38207              
38208         }
38209         this.beginUpdate();
38210         // add children..
38211         var region = '';
38212         var abn = {};
38213         Roo.each(xitems, function(i)  {
38214             region = nb && i.region ? i.region : false;
38215             
38216             var add = ret.addxtype(i);
38217            
38218             if (region) {
38219                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38220                 if (!i.background) {
38221                     abn[region] = nb[region] ;
38222                 }
38223             }
38224             
38225         });
38226         this.endUpdate();
38227
38228         // make the last non-background panel active..
38229         //if (nb) { Roo.log(abn); }
38230         if (nb) {
38231             
38232             for(var r in abn) {
38233                 region = this.getRegion(r);
38234                 if (region) {
38235                     // tried using nb[r], but it does not work..
38236                      
38237                     region.showPanel(abn[r]);
38238                    
38239                 }
38240             }
38241         }
38242         return ret;
38243         
38244     },
38245     
38246     
38247 // private
38248     factory : function(cfg)
38249     {
38250         
38251         var validRegions = Roo.bootstrap.layout.Border.regions;
38252
38253         var target = cfg.region;
38254         cfg.mgr = this;
38255         
38256         var r = Roo.bootstrap.layout;
38257         Roo.log(target);
38258         switch(target){
38259             case "north":
38260                 return new r.North(cfg);
38261             case "south":
38262                 return new r.South(cfg);
38263             case "east":
38264                 return new r.East(cfg);
38265             case "west":
38266                 return new r.West(cfg);
38267             case "center":
38268                 return new r.Center(cfg);
38269         }
38270         throw 'Layout region "'+target+'" not supported.';
38271     }
38272     
38273     
38274 });
38275  /*
38276  * Based on:
38277  * Ext JS Library 1.1.1
38278  * Copyright(c) 2006-2007, Ext JS, LLC.
38279  *
38280  * Originally Released Under LGPL - original licence link has changed is not relivant.
38281  *
38282  * Fork - LGPL
38283  * <script type="text/javascript">
38284  */
38285  
38286 /**
38287  * @class Roo.bootstrap.layout.Basic
38288  * @extends Roo.util.Observable
38289  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38290  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38291  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38292  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38293  * @cfg {string}   region  the region that it inhabits..
38294  * @cfg {bool}   skipConfig skip config?
38295  * 
38296
38297  */
38298 Roo.bootstrap.layout.Basic = function(config){
38299     
38300     this.mgr = config.mgr;
38301     
38302     this.position = config.region;
38303     
38304     var skipConfig = config.skipConfig;
38305     
38306     this.events = {
38307         /**
38308          * @scope Roo.BasicLayoutRegion
38309          */
38310         
38311         /**
38312          * @event beforeremove
38313          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38314          * @param {Roo.LayoutRegion} this
38315          * @param {Roo.ContentPanel} panel The panel
38316          * @param {Object} e The cancel event object
38317          */
38318         "beforeremove" : true,
38319         /**
38320          * @event invalidated
38321          * Fires when the layout for this region is changed.
38322          * @param {Roo.LayoutRegion} this
38323          */
38324         "invalidated" : true,
38325         /**
38326          * @event visibilitychange
38327          * Fires when this region is shown or hidden 
38328          * @param {Roo.LayoutRegion} this
38329          * @param {Boolean} visibility true or false
38330          */
38331         "visibilitychange" : true,
38332         /**
38333          * @event paneladded
38334          * Fires when a panel is added. 
38335          * @param {Roo.LayoutRegion} this
38336          * @param {Roo.ContentPanel} panel The panel
38337          */
38338         "paneladded" : true,
38339         /**
38340          * @event panelremoved
38341          * Fires when a panel is removed. 
38342          * @param {Roo.LayoutRegion} this
38343          * @param {Roo.ContentPanel} panel The panel
38344          */
38345         "panelremoved" : true,
38346         /**
38347          * @event beforecollapse
38348          * Fires when this region before collapse.
38349          * @param {Roo.LayoutRegion} this
38350          */
38351         "beforecollapse" : true,
38352         /**
38353          * @event collapsed
38354          * Fires when this region is collapsed.
38355          * @param {Roo.LayoutRegion} this
38356          */
38357         "collapsed" : true,
38358         /**
38359          * @event expanded
38360          * Fires when this region is expanded.
38361          * @param {Roo.LayoutRegion} this
38362          */
38363         "expanded" : true,
38364         /**
38365          * @event slideshow
38366          * Fires when this region is slid into view.
38367          * @param {Roo.LayoutRegion} this
38368          */
38369         "slideshow" : true,
38370         /**
38371          * @event slidehide
38372          * Fires when this region slides out of view. 
38373          * @param {Roo.LayoutRegion} this
38374          */
38375         "slidehide" : true,
38376         /**
38377          * @event panelactivated
38378          * Fires when a panel is activated. 
38379          * @param {Roo.LayoutRegion} this
38380          * @param {Roo.ContentPanel} panel The activated panel
38381          */
38382         "panelactivated" : true,
38383         /**
38384          * @event resized
38385          * Fires when the user resizes this region. 
38386          * @param {Roo.LayoutRegion} this
38387          * @param {Number} newSize The new size (width for east/west, height for north/south)
38388          */
38389         "resized" : true
38390     };
38391     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38392     this.panels = new Roo.util.MixedCollection();
38393     this.panels.getKey = this.getPanelId.createDelegate(this);
38394     this.box = null;
38395     this.activePanel = null;
38396     // ensure listeners are added...
38397     
38398     if (config.listeners || config.events) {
38399         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38400             listeners : config.listeners || {},
38401             events : config.events || {}
38402         });
38403     }
38404     
38405     if(skipConfig !== true){
38406         this.applyConfig(config);
38407     }
38408 };
38409
38410 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38411 {
38412     getPanelId : function(p){
38413         return p.getId();
38414     },
38415     
38416     applyConfig : function(config){
38417         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38418         this.config = config;
38419         
38420     },
38421     
38422     /**
38423      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38424      * the width, for horizontal (north, south) the height.
38425      * @param {Number} newSize The new width or height
38426      */
38427     resizeTo : function(newSize){
38428         var el = this.el ? this.el :
38429                  (this.activePanel ? this.activePanel.getEl() : null);
38430         if(el){
38431             switch(this.position){
38432                 case "east":
38433                 case "west":
38434                     el.setWidth(newSize);
38435                     this.fireEvent("resized", this, newSize);
38436                 break;
38437                 case "north":
38438                 case "south":
38439                     el.setHeight(newSize);
38440                     this.fireEvent("resized", this, newSize);
38441                 break;                
38442             }
38443         }
38444     },
38445     
38446     getBox : function(){
38447         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38448     },
38449     
38450     getMargins : function(){
38451         return this.margins;
38452     },
38453     
38454     updateBox : function(box){
38455         this.box = box;
38456         var el = this.activePanel.getEl();
38457         el.dom.style.left = box.x + "px";
38458         el.dom.style.top = box.y + "px";
38459         this.activePanel.setSize(box.width, box.height);
38460     },
38461     
38462     /**
38463      * Returns the container element for this region.
38464      * @return {Roo.Element}
38465      */
38466     getEl : function(){
38467         return this.activePanel;
38468     },
38469     
38470     /**
38471      * Returns true if this region is currently visible.
38472      * @return {Boolean}
38473      */
38474     isVisible : function(){
38475         return this.activePanel ? true : false;
38476     },
38477     
38478     setActivePanel : function(panel){
38479         panel = this.getPanel(panel);
38480         if(this.activePanel && this.activePanel != panel){
38481             this.activePanel.setActiveState(false);
38482             this.activePanel.getEl().setLeftTop(-10000,-10000);
38483         }
38484         this.activePanel = panel;
38485         panel.setActiveState(true);
38486         if(this.box){
38487             panel.setSize(this.box.width, this.box.height);
38488         }
38489         this.fireEvent("panelactivated", this, panel);
38490         this.fireEvent("invalidated");
38491     },
38492     
38493     /**
38494      * Show the specified panel.
38495      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38496      * @return {Roo.ContentPanel} The shown panel or null
38497      */
38498     showPanel : function(panel){
38499         panel = this.getPanel(panel);
38500         if(panel){
38501             this.setActivePanel(panel);
38502         }
38503         return panel;
38504     },
38505     
38506     /**
38507      * Get the active panel for this region.
38508      * @return {Roo.ContentPanel} The active panel or null
38509      */
38510     getActivePanel : function(){
38511         return this.activePanel;
38512     },
38513     
38514     /**
38515      * Add the passed ContentPanel(s)
38516      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38517      * @return {Roo.ContentPanel} The panel added (if only one was added)
38518      */
38519     add : function(panel){
38520         if(arguments.length > 1){
38521             for(var i = 0, len = arguments.length; i < len; i++) {
38522                 this.add(arguments[i]);
38523             }
38524             return null;
38525         }
38526         if(this.hasPanel(panel)){
38527             this.showPanel(panel);
38528             return panel;
38529         }
38530         var el = panel.getEl();
38531         if(el.dom.parentNode != this.mgr.el.dom){
38532             this.mgr.el.dom.appendChild(el.dom);
38533         }
38534         if(panel.setRegion){
38535             panel.setRegion(this);
38536         }
38537         this.panels.add(panel);
38538         el.setStyle("position", "absolute");
38539         if(!panel.background){
38540             this.setActivePanel(panel);
38541             if(this.config.initialSize && this.panels.getCount()==1){
38542                 this.resizeTo(this.config.initialSize);
38543             }
38544         }
38545         this.fireEvent("paneladded", this, panel);
38546         return panel;
38547     },
38548     
38549     /**
38550      * Returns true if the panel is in this region.
38551      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38552      * @return {Boolean}
38553      */
38554     hasPanel : function(panel){
38555         if(typeof panel == "object"){ // must be panel obj
38556             panel = panel.getId();
38557         }
38558         return this.getPanel(panel) ? true : false;
38559     },
38560     
38561     /**
38562      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38563      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38564      * @param {Boolean} preservePanel Overrides the config preservePanel option
38565      * @return {Roo.ContentPanel} The panel that was removed
38566      */
38567     remove : function(panel, preservePanel){
38568         panel = this.getPanel(panel);
38569         if(!panel){
38570             return null;
38571         }
38572         var e = {};
38573         this.fireEvent("beforeremove", this, panel, e);
38574         if(e.cancel === true){
38575             return null;
38576         }
38577         var panelId = panel.getId();
38578         this.panels.removeKey(panelId);
38579         return panel;
38580     },
38581     
38582     /**
38583      * Returns the panel specified or null if it's not in this region.
38584      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38585      * @return {Roo.ContentPanel}
38586      */
38587     getPanel : function(id){
38588         if(typeof id == "object"){ // must be panel obj
38589             return id;
38590         }
38591         return this.panels.get(id);
38592     },
38593     
38594     /**
38595      * Returns this regions position (north/south/east/west/center).
38596      * @return {String} 
38597      */
38598     getPosition: function(){
38599         return this.position;    
38600     }
38601 });/*
38602  * Based on:
38603  * Ext JS Library 1.1.1
38604  * Copyright(c) 2006-2007, Ext JS, LLC.
38605  *
38606  * Originally Released Under LGPL - original licence link has changed is not relivant.
38607  *
38608  * Fork - LGPL
38609  * <script type="text/javascript">
38610  */
38611  
38612 /**
38613  * @class Roo.bootstrap.layout.Region
38614  * @extends Roo.bootstrap.layout.Basic
38615  * This class represents a region in a layout manager.
38616  
38617  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38618  * @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})
38619  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38620  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38621  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38622  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38623  * @cfg {String}    title           The title for the region (overrides panel titles)
38624  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38625  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38626  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38627  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38628  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38629  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38630  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38631  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38632  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38633  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38634
38635  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38636  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38637  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38638  * @cfg {Number}    width           For East/West panels
38639  * @cfg {Number}    height          For North/South panels
38640  * @cfg {Boolean}   split           To show the splitter
38641  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38642  * 
38643  * @cfg {string}   cls             Extra CSS classes to add to region
38644  * 
38645  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38646  * @cfg {string}   region  the region that it inhabits..
38647  *
38648
38649  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38650  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38651
38652  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38653  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38654  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38655  */
38656 Roo.bootstrap.layout.Region = function(config)
38657 {
38658     this.applyConfig(config);
38659
38660     var mgr = config.mgr;
38661     var pos = config.region;
38662     config.skipConfig = true;
38663     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38664     
38665     if (mgr.el) {
38666         this.onRender(mgr.el);   
38667     }
38668      
38669     this.visible = true;
38670     this.collapsed = false;
38671     this.unrendered_panels = [];
38672 };
38673
38674 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38675
38676     position: '', // set by wrapper (eg. north/south etc..)
38677     unrendered_panels : null,  // unrendered panels.
38678     
38679     tabPosition : false,
38680     
38681     mgr: false, // points to 'Border'
38682     
38683     
38684     createBody : function(){
38685         /** This region's body element 
38686         * @type Roo.Element */
38687         this.bodyEl = this.el.createChild({
38688                 tag: "div",
38689                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38690         });
38691     },
38692
38693     onRender: function(ctr, pos)
38694     {
38695         var dh = Roo.DomHelper;
38696         /** This region's container element 
38697         * @type Roo.Element */
38698         this.el = dh.append(ctr.dom, {
38699                 tag: "div",
38700                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38701             }, true);
38702         /** This region's title element 
38703         * @type Roo.Element */
38704     
38705         this.titleEl = dh.append(this.el.dom,  {
38706                 tag: "div",
38707                 unselectable: "on",
38708                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38709                 children:[
38710                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38711                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38712                 ]
38713             }, true);
38714         
38715         this.titleEl.enableDisplayMode();
38716         /** This region's title text element 
38717         * @type HTMLElement */
38718         this.titleTextEl = this.titleEl.dom.firstChild;
38719         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38720         /*
38721         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38722         this.closeBtn.enableDisplayMode();
38723         this.closeBtn.on("click", this.closeClicked, this);
38724         this.closeBtn.hide();
38725     */
38726         this.createBody(this.config);
38727         if(this.config.hideWhenEmpty){
38728             this.hide();
38729             this.on("paneladded", this.validateVisibility, this);
38730             this.on("panelremoved", this.validateVisibility, this);
38731         }
38732         if(this.autoScroll){
38733             this.bodyEl.setStyle("overflow", "auto");
38734         }else{
38735             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38736         }
38737         //if(c.titlebar !== false){
38738             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38739                 this.titleEl.hide();
38740             }else{
38741                 this.titleEl.show();
38742                 if(this.config.title){
38743                     this.titleTextEl.innerHTML = this.config.title;
38744                 }
38745             }
38746         //}
38747         if(this.config.collapsed){
38748             this.collapse(true);
38749         }
38750         if(this.config.hidden){
38751             this.hide();
38752         }
38753         
38754         if (this.unrendered_panels && this.unrendered_panels.length) {
38755             for (var i =0;i< this.unrendered_panels.length; i++) {
38756                 this.add(this.unrendered_panels[i]);
38757             }
38758             this.unrendered_panels = null;
38759             
38760         }
38761         
38762     },
38763     
38764     applyConfig : function(c)
38765     {
38766         /*
38767          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38768             var dh = Roo.DomHelper;
38769             if(c.titlebar !== false){
38770                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38771                 this.collapseBtn.on("click", this.collapse, this);
38772                 this.collapseBtn.enableDisplayMode();
38773                 /*
38774                 if(c.showPin === true || this.showPin){
38775                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38776                     this.stickBtn.enableDisplayMode();
38777                     this.stickBtn.on("click", this.expand, this);
38778                     this.stickBtn.hide();
38779                 }
38780                 
38781             }
38782             */
38783             /** This region's collapsed element
38784             * @type Roo.Element */
38785             /*
38786              *
38787             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38788                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38789             ]}, true);
38790             
38791             if(c.floatable !== false){
38792                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38793                this.collapsedEl.on("click", this.collapseClick, this);
38794             }
38795
38796             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38797                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38798                    id: "message", unselectable: "on", style:{"float":"left"}});
38799                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38800              }
38801             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38802             this.expandBtn.on("click", this.expand, this);
38803             
38804         }
38805         
38806         if(this.collapseBtn){
38807             this.collapseBtn.setVisible(c.collapsible == true);
38808         }
38809         
38810         this.cmargins = c.cmargins || this.cmargins ||
38811                          (this.position == "west" || this.position == "east" ?
38812                              {top: 0, left: 2, right:2, bottom: 0} :
38813                              {top: 2, left: 0, right:0, bottom: 2});
38814         */
38815         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38816         
38817         
38818         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38819         
38820         this.autoScroll = c.autoScroll || false;
38821         
38822         
38823        
38824         
38825         this.duration = c.duration || .30;
38826         this.slideDuration = c.slideDuration || .45;
38827         this.config = c;
38828        
38829     },
38830     /**
38831      * Returns true if this region is currently visible.
38832      * @return {Boolean}
38833      */
38834     isVisible : function(){
38835         return this.visible;
38836     },
38837
38838     /**
38839      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38840      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38841      */
38842     //setCollapsedTitle : function(title){
38843     //    title = title || "&#160;";
38844      //   if(this.collapsedTitleTextEl){
38845       //      this.collapsedTitleTextEl.innerHTML = title;
38846        // }
38847     //},
38848
38849     getBox : function(){
38850         var b;
38851       //  if(!this.collapsed){
38852             b = this.el.getBox(false, true);
38853        // }else{
38854           //  b = this.collapsedEl.getBox(false, true);
38855         //}
38856         return b;
38857     },
38858
38859     getMargins : function(){
38860         return this.margins;
38861         //return this.collapsed ? this.cmargins : this.margins;
38862     },
38863 /*
38864     highlight : function(){
38865         this.el.addClass("x-layout-panel-dragover");
38866     },
38867
38868     unhighlight : function(){
38869         this.el.removeClass("x-layout-panel-dragover");
38870     },
38871 */
38872     updateBox : function(box)
38873     {
38874         if (!this.bodyEl) {
38875             return; // not rendered yet..
38876         }
38877         
38878         this.box = box;
38879         if(!this.collapsed){
38880             this.el.dom.style.left = box.x + "px";
38881             this.el.dom.style.top = box.y + "px";
38882             this.updateBody(box.width, box.height);
38883         }else{
38884             this.collapsedEl.dom.style.left = box.x + "px";
38885             this.collapsedEl.dom.style.top = box.y + "px";
38886             this.collapsedEl.setSize(box.width, box.height);
38887         }
38888         if(this.tabs){
38889             this.tabs.autoSizeTabs();
38890         }
38891     },
38892
38893     updateBody : function(w, h)
38894     {
38895         if(w !== null){
38896             this.el.setWidth(w);
38897             w -= this.el.getBorderWidth("rl");
38898             if(this.config.adjustments){
38899                 w += this.config.adjustments[0];
38900             }
38901         }
38902         if(h !== null && h > 0){
38903             this.el.setHeight(h);
38904             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38905             h -= this.el.getBorderWidth("tb");
38906             if(this.config.adjustments){
38907                 h += this.config.adjustments[1];
38908             }
38909             this.bodyEl.setHeight(h);
38910             if(this.tabs){
38911                 h = this.tabs.syncHeight(h);
38912             }
38913         }
38914         if(this.panelSize){
38915             w = w !== null ? w : this.panelSize.width;
38916             h = h !== null ? h : this.panelSize.height;
38917         }
38918         if(this.activePanel){
38919             var el = this.activePanel.getEl();
38920             w = w !== null ? w : el.getWidth();
38921             h = h !== null ? h : el.getHeight();
38922             this.panelSize = {width: w, height: h};
38923             this.activePanel.setSize(w, h);
38924         }
38925         if(Roo.isIE && this.tabs){
38926             this.tabs.el.repaint();
38927         }
38928     },
38929
38930     /**
38931      * Returns the container element for this region.
38932      * @return {Roo.Element}
38933      */
38934     getEl : function(){
38935         return this.el;
38936     },
38937
38938     /**
38939      * Hides this region.
38940      */
38941     hide : function(){
38942         //if(!this.collapsed){
38943             this.el.dom.style.left = "-2000px";
38944             this.el.hide();
38945         //}else{
38946          //   this.collapsedEl.dom.style.left = "-2000px";
38947          //   this.collapsedEl.hide();
38948        // }
38949         this.visible = false;
38950         this.fireEvent("visibilitychange", this, false);
38951     },
38952
38953     /**
38954      * Shows this region if it was previously hidden.
38955      */
38956     show : function(){
38957         //if(!this.collapsed){
38958             this.el.show();
38959         //}else{
38960         //    this.collapsedEl.show();
38961        // }
38962         this.visible = true;
38963         this.fireEvent("visibilitychange", this, true);
38964     },
38965 /*
38966     closeClicked : function(){
38967         if(this.activePanel){
38968             this.remove(this.activePanel);
38969         }
38970     },
38971
38972     collapseClick : function(e){
38973         if(this.isSlid){
38974            e.stopPropagation();
38975            this.slideIn();
38976         }else{
38977            e.stopPropagation();
38978            this.slideOut();
38979         }
38980     },
38981 */
38982     /**
38983      * Collapses this region.
38984      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38985      */
38986     /*
38987     collapse : function(skipAnim, skipCheck = false){
38988         if(this.collapsed) {
38989             return;
38990         }
38991         
38992         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38993             
38994             this.collapsed = true;
38995             if(this.split){
38996                 this.split.el.hide();
38997             }
38998             if(this.config.animate && skipAnim !== true){
38999                 this.fireEvent("invalidated", this);
39000                 this.animateCollapse();
39001             }else{
39002                 this.el.setLocation(-20000,-20000);
39003                 this.el.hide();
39004                 this.collapsedEl.show();
39005                 this.fireEvent("collapsed", this);
39006                 this.fireEvent("invalidated", this);
39007             }
39008         }
39009         
39010     },
39011 */
39012     animateCollapse : function(){
39013         // overridden
39014     },
39015
39016     /**
39017      * Expands this region if it was previously collapsed.
39018      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39019      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39020      */
39021     /*
39022     expand : function(e, skipAnim){
39023         if(e) {
39024             e.stopPropagation();
39025         }
39026         if(!this.collapsed || this.el.hasActiveFx()) {
39027             return;
39028         }
39029         if(this.isSlid){
39030             this.afterSlideIn();
39031             skipAnim = true;
39032         }
39033         this.collapsed = false;
39034         if(this.config.animate && skipAnim !== true){
39035             this.animateExpand();
39036         }else{
39037             this.el.show();
39038             if(this.split){
39039                 this.split.el.show();
39040             }
39041             this.collapsedEl.setLocation(-2000,-2000);
39042             this.collapsedEl.hide();
39043             this.fireEvent("invalidated", this);
39044             this.fireEvent("expanded", this);
39045         }
39046     },
39047 */
39048     animateExpand : function(){
39049         // overridden
39050     },
39051
39052     initTabs : function()
39053     {
39054         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39055         
39056         var ts = new Roo.bootstrap.panel.Tabs({
39057             el: this.bodyEl.dom,
39058             region : this,
39059             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39060             disableTooltips: this.config.disableTabTips,
39061             toolbar : this.config.toolbar
39062         });
39063         
39064         if(this.config.hideTabs){
39065             ts.stripWrap.setDisplayed(false);
39066         }
39067         this.tabs = ts;
39068         ts.resizeTabs = this.config.resizeTabs === true;
39069         ts.minTabWidth = this.config.minTabWidth || 40;
39070         ts.maxTabWidth = this.config.maxTabWidth || 250;
39071         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39072         ts.monitorResize = false;
39073         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39074         ts.bodyEl.addClass('roo-layout-tabs-body');
39075         this.panels.each(this.initPanelAsTab, this);
39076     },
39077
39078     initPanelAsTab : function(panel){
39079         var ti = this.tabs.addTab(
39080             panel.getEl().id,
39081             panel.getTitle(),
39082             null,
39083             this.config.closeOnTab && panel.isClosable(),
39084             panel.tpl
39085         );
39086         if(panel.tabTip !== undefined){
39087             ti.setTooltip(panel.tabTip);
39088         }
39089         ti.on("activate", function(){
39090               this.setActivePanel(panel);
39091         }, this);
39092         
39093         if(this.config.closeOnTab){
39094             ti.on("beforeclose", function(t, e){
39095                 e.cancel = true;
39096                 this.remove(panel);
39097             }, this);
39098         }
39099         
39100         panel.tabItem = ti;
39101         
39102         return ti;
39103     },
39104
39105     updatePanelTitle : function(panel, title)
39106     {
39107         if(this.activePanel == panel){
39108             this.updateTitle(title);
39109         }
39110         if(this.tabs){
39111             var ti = this.tabs.getTab(panel.getEl().id);
39112             ti.setText(title);
39113             if(panel.tabTip !== undefined){
39114                 ti.setTooltip(panel.tabTip);
39115             }
39116         }
39117     },
39118
39119     updateTitle : function(title){
39120         if(this.titleTextEl && !this.config.title){
39121             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39122         }
39123     },
39124
39125     setActivePanel : function(panel)
39126     {
39127         panel = this.getPanel(panel);
39128         if(this.activePanel && this.activePanel != panel){
39129             if(this.activePanel.setActiveState(false) === false){
39130                 return;
39131             }
39132         }
39133         this.activePanel = panel;
39134         panel.setActiveState(true);
39135         if(this.panelSize){
39136             panel.setSize(this.panelSize.width, this.panelSize.height);
39137         }
39138         if(this.closeBtn){
39139             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39140         }
39141         this.updateTitle(panel.getTitle());
39142         if(this.tabs){
39143             this.fireEvent("invalidated", this);
39144         }
39145         this.fireEvent("panelactivated", this, panel);
39146     },
39147
39148     /**
39149      * Shows the specified panel.
39150      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39151      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39152      */
39153     showPanel : function(panel)
39154     {
39155         panel = this.getPanel(panel);
39156         if(panel){
39157             if(this.tabs){
39158                 var tab = this.tabs.getTab(panel.getEl().id);
39159                 if(tab.isHidden()){
39160                     this.tabs.unhideTab(tab.id);
39161                 }
39162                 tab.activate();
39163             }else{
39164                 this.setActivePanel(panel);
39165             }
39166         }
39167         return panel;
39168     },
39169
39170     /**
39171      * Get the active panel for this region.
39172      * @return {Roo.ContentPanel} The active panel or null
39173      */
39174     getActivePanel : function(){
39175         return this.activePanel;
39176     },
39177
39178     validateVisibility : function(){
39179         if(this.panels.getCount() < 1){
39180             this.updateTitle("&#160;");
39181             this.closeBtn.hide();
39182             this.hide();
39183         }else{
39184             if(!this.isVisible()){
39185                 this.show();
39186             }
39187         }
39188     },
39189
39190     /**
39191      * Adds the passed ContentPanel(s) to this region.
39192      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39193      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39194      */
39195     add : function(panel)
39196     {
39197         if(arguments.length > 1){
39198             for(var i = 0, len = arguments.length; i < len; i++) {
39199                 this.add(arguments[i]);
39200             }
39201             return null;
39202         }
39203         
39204         // if we have not been rendered yet, then we can not really do much of this..
39205         if (!this.bodyEl) {
39206             this.unrendered_panels.push(panel);
39207             return panel;
39208         }
39209         
39210         
39211         
39212         
39213         if(this.hasPanel(panel)){
39214             this.showPanel(panel);
39215             return panel;
39216         }
39217         panel.setRegion(this);
39218         this.panels.add(panel);
39219        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39220             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39221             // and hide them... ???
39222             this.bodyEl.dom.appendChild(panel.getEl().dom);
39223             if(panel.background !== true){
39224                 this.setActivePanel(panel);
39225             }
39226             this.fireEvent("paneladded", this, panel);
39227             return panel;
39228         }
39229         */
39230         if(!this.tabs){
39231             this.initTabs();
39232         }else{
39233             this.initPanelAsTab(panel);
39234         }
39235         
39236         
39237         if(panel.background !== true){
39238             this.tabs.activate(panel.getEl().id);
39239         }
39240         this.fireEvent("paneladded", this, panel);
39241         return panel;
39242     },
39243
39244     /**
39245      * Hides the tab for the specified panel.
39246      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39247      */
39248     hidePanel : function(panel){
39249         if(this.tabs && (panel = this.getPanel(panel))){
39250             this.tabs.hideTab(panel.getEl().id);
39251         }
39252     },
39253
39254     /**
39255      * Unhides the tab for a previously hidden panel.
39256      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39257      */
39258     unhidePanel : function(panel){
39259         if(this.tabs && (panel = this.getPanel(panel))){
39260             this.tabs.unhideTab(panel.getEl().id);
39261         }
39262     },
39263
39264     clearPanels : function(){
39265         while(this.panels.getCount() > 0){
39266              this.remove(this.panels.first());
39267         }
39268     },
39269
39270     /**
39271      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39272      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39273      * @param {Boolean} preservePanel Overrides the config preservePanel option
39274      * @return {Roo.ContentPanel} The panel that was removed
39275      */
39276     remove : function(panel, preservePanel)
39277     {
39278         panel = this.getPanel(panel);
39279         if(!panel){
39280             return null;
39281         }
39282         var e = {};
39283         this.fireEvent("beforeremove", this, panel, e);
39284         if(e.cancel === true){
39285             return null;
39286         }
39287         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39288         var panelId = panel.getId();
39289         this.panels.removeKey(panelId);
39290         if(preservePanel){
39291             document.body.appendChild(panel.getEl().dom);
39292         }
39293         if(this.tabs){
39294             this.tabs.removeTab(panel.getEl().id);
39295         }else if (!preservePanel){
39296             this.bodyEl.dom.removeChild(panel.getEl().dom);
39297         }
39298         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39299             var p = this.panels.first();
39300             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39301             tempEl.appendChild(p.getEl().dom);
39302             this.bodyEl.update("");
39303             this.bodyEl.dom.appendChild(p.getEl().dom);
39304             tempEl = null;
39305             this.updateTitle(p.getTitle());
39306             this.tabs = null;
39307             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39308             this.setActivePanel(p);
39309         }
39310         panel.setRegion(null);
39311         if(this.activePanel == panel){
39312             this.activePanel = null;
39313         }
39314         if(this.config.autoDestroy !== false && preservePanel !== true){
39315             try{panel.destroy();}catch(e){}
39316         }
39317         this.fireEvent("panelremoved", this, panel);
39318         return panel;
39319     },
39320
39321     /**
39322      * Returns the TabPanel component used by this region
39323      * @return {Roo.TabPanel}
39324      */
39325     getTabs : function(){
39326         return this.tabs;
39327     },
39328
39329     createTool : function(parentEl, className){
39330         var btn = Roo.DomHelper.append(parentEl, {
39331             tag: "div",
39332             cls: "x-layout-tools-button",
39333             children: [ {
39334                 tag: "div",
39335                 cls: "roo-layout-tools-button-inner " + className,
39336                 html: "&#160;"
39337             }]
39338         }, true);
39339         btn.addClassOnOver("roo-layout-tools-button-over");
39340         return btn;
39341     }
39342 });/*
39343  * Based on:
39344  * Ext JS Library 1.1.1
39345  * Copyright(c) 2006-2007, Ext JS, LLC.
39346  *
39347  * Originally Released Under LGPL - original licence link has changed is not relivant.
39348  *
39349  * Fork - LGPL
39350  * <script type="text/javascript">
39351  */
39352  
39353
39354
39355 /**
39356  * @class Roo.SplitLayoutRegion
39357  * @extends Roo.LayoutRegion
39358  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39359  */
39360 Roo.bootstrap.layout.Split = function(config){
39361     this.cursor = config.cursor;
39362     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39363 };
39364
39365 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39366 {
39367     splitTip : "Drag to resize.",
39368     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39369     useSplitTips : false,
39370
39371     applyConfig : function(config){
39372         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39373     },
39374     
39375     onRender : function(ctr,pos) {
39376         
39377         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39378         if(!this.config.split){
39379             return;
39380         }
39381         if(!this.split){
39382             
39383             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39384                             tag: "div",
39385                             id: this.el.id + "-split",
39386                             cls: "roo-layout-split roo-layout-split-"+this.position,
39387                             html: "&#160;"
39388             });
39389             /** The SplitBar for this region 
39390             * @type Roo.SplitBar */
39391             // does not exist yet...
39392             Roo.log([this.position, this.orientation]);
39393             
39394             this.split = new Roo.bootstrap.SplitBar({
39395                 dragElement : splitEl,
39396                 resizingElement: this.el,
39397                 orientation : this.orientation
39398             });
39399             
39400             this.split.on("moved", this.onSplitMove, this);
39401             this.split.useShim = this.config.useShim === true;
39402             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39403             if(this.useSplitTips){
39404                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39405             }
39406             //if(config.collapsible){
39407             //    this.split.el.on("dblclick", this.collapse,  this);
39408             //}
39409         }
39410         if(typeof this.config.minSize != "undefined"){
39411             this.split.minSize = this.config.minSize;
39412         }
39413         if(typeof this.config.maxSize != "undefined"){
39414             this.split.maxSize = this.config.maxSize;
39415         }
39416         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39417             this.hideSplitter();
39418         }
39419         
39420     },
39421
39422     getHMaxSize : function(){
39423          var cmax = this.config.maxSize || 10000;
39424          var center = this.mgr.getRegion("center");
39425          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39426     },
39427
39428     getVMaxSize : function(){
39429          var cmax = this.config.maxSize || 10000;
39430          var center = this.mgr.getRegion("center");
39431          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39432     },
39433
39434     onSplitMove : function(split, newSize){
39435         this.fireEvent("resized", this, newSize);
39436     },
39437     
39438     /** 
39439      * Returns the {@link Roo.SplitBar} for this region.
39440      * @return {Roo.SplitBar}
39441      */
39442     getSplitBar : function(){
39443         return this.split;
39444     },
39445     
39446     hide : function(){
39447         this.hideSplitter();
39448         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39449     },
39450
39451     hideSplitter : function(){
39452         if(this.split){
39453             this.split.el.setLocation(-2000,-2000);
39454             this.split.el.hide();
39455         }
39456     },
39457
39458     show : function(){
39459         if(this.split){
39460             this.split.el.show();
39461         }
39462         Roo.bootstrap.layout.Split.superclass.show.call(this);
39463     },
39464     
39465     beforeSlide: function(){
39466         if(Roo.isGecko){// firefox overflow auto bug workaround
39467             this.bodyEl.clip();
39468             if(this.tabs) {
39469                 this.tabs.bodyEl.clip();
39470             }
39471             if(this.activePanel){
39472                 this.activePanel.getEl().clip();
39473                 
39474                 if(this.activePanel.beforeSlide){
39475                     this.activePanel.beforeSlide();
39476                 }
39477             }
39478         }
39479     },
39480     
39481     afterSlide : function(){
39482         if(Roo.isGecko){// firefox overflow auto bug workaround
39483             this.bodyEl.unclip();
39484             if(this.tabs) {
39485                 this.tabs.bodyEl.unclip();
39486             }
39487             if(this.activePanel){
39488                 this.activePanel.getEl().unclip();
39489                 if(this.activePanel.afterSlide){
39490                     this.activePanel.afterSlide();
39491                 }
39492             }
39493         }
39494     },
39495
39496     initAutoHide : function(){
39497         if(this.autoHide !== false){
39498             if(!this.autoHideHd){
39499                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39500                 this.autoHideHd = {
39501                     "mouseout": function(e){
39502                         if(!e.within(this.el, true)){
39503                             st.delay(500);
39504                         }
39505                     },
39506                     "mouseover" : function(e){
39507                         st.cancel();
39508                     },
39509                     scope : this
39510                 };
39511             }
39512             this.el.on(this.autoHideHd);
39513         }
39514     },
39515
39516     clearAutoHide : function(){
39517         if(this.autoHide !== false){
39518             this.el.un("mouseout", this.autoHideHd.mouseout);
39519             this.el.un("mouseover", this.autoHideHd.mouseover);
39520         }
39521     },
39522
39523     clearMonitor : function(){
39524         Roo.get(document).un("click", this.slideInIf, this);
39525     },
39526
39527     // these names are backwards but not changed for compat
39528     slideOut : function(){
39529         if(this.isSlid || this.el.hasActiveFx()){
39530             return;
39531         }
39532         this.isSlid = true;
39533         if(this.collapseBtn){
39534             this.collapseBtn.hide();
39535         }
39536         this.closeBtnState = this.closeBtn.getStyle('display');
39537         this.closeBtn.hide();
39538         if(this.stickBtn){
39539             this.stickBtn.show();
39540         }
39541         this.el.show();
39542         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39543         this.beforeSlide();
39544         this.el.setStyle("z-index", 10001);
39545         this.el.slideIn(this.getSlideAnchor(), {
39546             callback: function(){
39547                 this.afterSlide();
39548                 this.initAutoHide();
39549                 Roo.get(document).on("click", this.slideInIf, this);
39550                 this.fireEvent("slideshow", this);
39551             },
39552             scope: this,
39553             block: true
39554         });
39555     },
39556
39557     afterSlideIn : function(){
39558         this.clearAutoHide();
39559         this.isSlid = false;
39560         this.clearMonitor();
39561         this.el.setStyle("z-index", "");
39562         if(this.collapseBtn){
39563             this.collapseBtn.show();
39564         }
39565         this.closeBtn.setStyle('display', this.closeBtnState);
39566         if(this.stickBtn){
39567             this.stickBtn.hide();
39568         }
39569         this.fireEvent("slidehide", this);
39570     },
39571
39572     slideIn : function(cb){
39573         if(!this.isSlid || this.el.hasActiveFx()){
39574             Roo.callback(cb);
39575             return;
39576         }
39577         this.isSlid = false;
39578         this.beforeSlide();
39579         this.el.slideOut(this.getSlideAnchor(), {
39580             callback: function(){
39581                 this.el.setLeftTop(-10000, -10000);
39582                 this.afterSlide();
39583                 this.afterSlideIn();
39584                 Roo.callback(cb);
39585             },
39586             scope: this,
39587             block: true
39588         });
39589     },
39590     
39591     slideInIf : function(e){
39592         if(!e.within(this.el)){
39593             this.slideIn();
39594         }
39595     },
39596
39597     animateCollapse : function(){
39598         this.beforeSlide();
39599         this.el.setStyle("z-index", 20000);
39600         var anchor = this.getSlideAnchor();
39601         this.el.slideOut(anchor, {
39602             callback : function(){
39603                 this.el.setStyle("z-index", "");
39604                 this.collapsedEl.slideIn(anchor, {duration:.3});
39605                 this.afterSlide();
39606                 this.el.setLocation(-10000,-10000);
39607                 this.el.hide();
39608                 this.fireEvent("collapsed", this);
39609             },
39610             scope: this,
39611             block: true
39612         });
39613     },
39614
39615     animateExpand : function(){
39616         this.beforeSlide();
39617         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39618         this.el.setStyle("z-index", 20000);
39619         this.collapsedEl.hide({
39620             duration:.1
39621         });
39622         this.el.slideIn(this.getSlideAnchor(), {
39623             callback : function(){
39624                 this.el.setStyle("z-index", "");
39625                 this.afterSlide();
39626                 if(this.split){
39627                     this.split.el.show();
39628                 }
39629                 this.fireEvent("invalidated", this);
39630                 this.fireEvent("expanded", this);
39631             },
39632             scope: this,
39633             block: true
39634         });
39635     },
39636
39637     anchors : {
39638         "west" : "left",
39639         "east" : "right",
39640         "north" : "top",
39641         "south" : "bottom"
39642     },
39643
39644     sanchors : {
39645         "west" : "l",
39646         "east" : "r",
39647         "north" : "t",
39648         "south" : "b"
39649     },
39650
39651     canchors : {
39652         "west" : "tl-tr",
39653         "east" : "tr-tl",
39654         "north" : "tl-bl",
39655         "south" : "bl-tl"
39656     },
39657
39658     getAnchor : function(){
39659         return this.anchors[this.position];
39660     },
39661
39662     getCollapseAnchor : function(){
39663         return this.canchors[this.position];
39664     },
39665
39666     getSlideAnchor : function(){
39667         return this.sanchors[this.position];
39668     },
39669
39670     getAlignAdj : function(){
39671         var cm = this.cmargins;
39672         switch(this.position){
39673             case "west":
39674                 return [0, 0];
39675             break;
39676             case "east":
39677                 return [0, 0];
39678             break;
39679             case "north":
39680                 return [0, 0];
39681             break;
39682             case "south":
39683                 return [0, 0];
39684             break;
39685         }
39686     },
39687
39688     getExpandAdj : function(){
39689         var c = this.collapsedEl, cm = this.cmargins;
39690         switch(this.position){
39691             case "west":
39692                 return [-(cm.right+c.getWidth()+cm.left), 0];
39693             break;
39694             case "east":
39695                 return [cm.right+c.getWidth()+cm.left, 0];
39696             break;
39697             case "north":
39698                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39699             break;
39700             case "south":
39701                 return [0, cm.top+cm.bottom+c.getHeight()];
39702             break;
39703         }
39704     }
39705 });/*
39706  * Based on:
39707  * Ext JS Library 1.1.1
39708  * Copyright(c) 2006-2007, Ext JS, LLC.
39709  *
39710  * Originally Released Under LGPL - original licence link has changed is not relivant.
39711  *
39712  * Fork - LGPL
39713  * <script type="text/javascript">
39714  */
39715 /*
39716  * These classes are private internal classes
39717  */
39718 Roo.bootstrap.layout.Center = function(config){
39719     config.region = "center";
39720     Roo.bootstrap.layout.Region.call(this, config);
39721     this.visible = true;
39722     this.minWidth = config.minWidth || 20;
39723     this.minHeight = config.minHeight || 20;
39724 };
39725
39726 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39727     hide : function(){
39728         // center panel can't be hidden
39729     },
39730     
39731     show : function(){
39732         // center panel can't be hidden
39733     },
39734     
39735     getMinWidth: function(){
39736         return this.minWidth;
39737     },
39738     
39739     getMinHeight: function(){
39740         return this.minHeight;
39741     }
39742 });
39743
39744
39745
39746
39747  
39748
39749
39750
39751
39752
39753
39754 Roo.bootstrap.layout.North = function(config)
39755 {
39756     config.region = 'north';
39757     config.cursor = 'n-resize';
39758     
39759     Roo.bootstrap.layout.Split.call(this, config);
39760     
39761     
39762     if(this.split){
39763         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39764         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39765         this.split.el.addClass("roo-layout-split-v");
39766     }
39767     //var size = config.initialSize || config.height;
39768     //if(this.el && typeof size != "undefined"){
39769     //    this.el.setHeight(size);
39770     //}
39771 };
39772 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39773 {
39774     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39775      
39776      
39777     onRender : function(ctr, pos)
39778     {
39779         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39780         var size = this.config.initialSize || this.config.height;
39781         if(this.el && typeof size != "undefined"){
39782             this.el.setHeight(size);
39783         }
39784     
39785     },
39786     
39787     getBox : function(){
39788         if(this.collapsed){
39789             return this.collapsedEl.getBox();
39790         }
39791         var box = this.el.getBox();
39792         if(this.split){
39793             box.height += this.split.el.getHeight();
39794         }
39795         return box;
39796     },
39797     
39798     updateBox : function(box){
39799         if(this.split && !this.collapsed){
39800             box.height -= this.split.el.getHeight();
39801             this.split.el.setLeft(box.x);
39802             this.split.el.setTop(box.y+box.height);
39803             this.split.el.setWidth(box.width);
39804         }
39805         if(this.collapsed){
39806             this.updateBody(box.width, null);
39807         }
39808         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39809     }
39810 });
39811
39812
39813
39814
39815
39816 Roo.bootstrap.layout.South = function(config){
39817     config.region = 'south';
39818     config.cursor = 's-resize';
39819     Roo.bootstrap.layout.Split.call(this, config);
39820     if(this.split){
39821         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39822         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39823         this.split.el.addClass("roo-layout-split-v");
39824     }
39825     
39826 };
39827
39828 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39829     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39830     
39831     onRender : function(ctr, pos)
39832     {
39833         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39834         var size = this.config.initialSize || this.config.height;
39835         if(this.el && typeof size != "undefined"){
39836             this.el.setHeight(size);
39837         }
39838     
39839     },
39840     
39841     getBox : function(){
39842         if(this.collapsed){
39843             return this.collapsedEl.getBox();
39844         }
39845         var box = this.el.getBox();
39846         if(this.split){
39847             var sh = this.split.el.getHeight();
39848             box.height += sh;
39849             box.y -= sh;
39850         }
39851         return box;
39852     },
39853     
39854     updateBox : function(box){
39855         if(this.split && !this.collapsed){
39856             var sh = this.split.el.getHeight();
39857             box.height -= sh;
39858             box.y += sh;
39859             this.split.el.setLeft(box.x);
39860             this.split.el.setTop(box.y-sh);
39861             this.split.el.setWidth(box.width);
39862         }
39863         if(this.collapsed){
39864             this.updateBody(box.width, null);
39865         }
39866         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39867     }
39868 });
39869
39870 Roo.bootstrap.layout.East = function(config){
39871     config.region = "east";
39872     config.cursor = "e-resize";
39873     Roo.bootstrap.layout.Split.call(this, config);
39874     if(this.split){
39875         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39876         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39877         this.split.el.addClass("roo-layout-split-h");
39878     }
39879     
39880 };
39881 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39882     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39883     
39884     onRender : function(ctr, pos)
39885     {
39886         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39887         var size = this.config.initialSize || this.config.width;
39888         if(this.el && typeof size != "undefined"){
39889             this.el.setWidth(size);
39890         }
39891     
39892     },
39893     
39894     getBox : function(){
39895         if(this.collapsed){
39896             return this.collapsedEl.getBox();
39897         }
39898         var box = this.el.getBox();
39899         if(this.split){
39900             var sw = this.split.el.getWidth();
39901             box.width += sw;
39902             box.x -= sw;
39903         }
39904         return box;
39905     },
39906
39907     updateBox : function(box){
39908         if(this.split && !this.collapsed){
39909             var sw = this.split.el.getWidth();
39910             box.width -= sw;
39911             this.split.el.setLeft(box.x);
39912             this.split.el.setTop(box.y);
39913             this.split.el.setHeight(box.height);
39914             box.x += sw;
39915         }
39916         if(this.collapsed){
39917             this.updateBody(null, box.height);
39918         }
39919         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39920     }
39921 });
39922
39923 Roo.bootstrap.layout.West = function(config){
39924     config.region = "west";
39925     config.cursor = "w-resize";
39926     
39927     Roo.bootstrap.layout.Split.call(this, config);
39928     if(this.split){
39929         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39930         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39931         this.split.el.addClass("roo-layout-split-h");
39932     }
39933     
39934 };
39935 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39936     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39937     
39938     onRender: function(ctr, pos)
39939     {
39940         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39941         var size = this.config.initialSize || this.config.width;
39942         if(typeof size != "undefined"){
39943             this.el.setWidth(size);
39944         }
39945     },
39946     
39947     getBox : function(){
39948         if(this.collapsed){
39949             return this.collapsedEl.getBox();
39950         }
39951         var box = this.el.getBox();
39952         if (box.width == 0) {
39953             box.width = this.config.width; // kludge?
39954         }
39955         if(this.split){
39956             box.width += this.split.el.getWidth();
39957         }
39958         return box;
39959     },
39960     
39961     updateBox : function(box){
39962         if(this.split && !this.collapsed){
39963             var sw = this.split.el.getWidth();
39964             box.width -= sw;
39965             this.split.el.setLeft(box.x+box.width);
39966             this.split.el.setTop(box.y);
39967             this.split.el.setHeight(box.height);
39968         }
39969         if(this.collapsed){
39970             this.updateBody(null, box.height);
39971         }
39972         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39973     }
39974 });Roo.namespace("Roo.bootstrap.panel");/*
39975  * Based on:
39976  * Ext JS Library 1.1.1
39977  * Copyright(c) 2006-2007, Ext JS, LLC.
39978  *
39979  * Originally Released Under LGPL - original licence link has changed is not relivant.
39980  *
39981  * Fork - LGPL
39982  * <script type="text/javascript">
39983  */
39984 /**
39985  * @class Roo.ContentPanel
39986  * @extends Roo.util.Observable
39987  * A basic ContentPanel element.
39988  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39989  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39990  * @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
39991  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39992  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39993  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39994  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39995  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39996  * @cfg {String} title          The title for this panel
39997  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39998  * @cfg {String} url            Calls {@link #setUrl} with this value
39999  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40000  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40001  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40002  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40003  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40004  * @cfg {Boolean} badges render the badges
40005  * @cfg {String} cls  extra classes to use  
40006  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40007
40008  * @constructor
40009  * Create a new ContentPanel.
40010  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40011  * @param {String/Object} config A string to set only the title or a config object
40012  * @param {String} content (optional) Set the HTML content for this panel
40013  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40014  */
40015 Roo.bootstrap.panel.Content = function( config){
40016     
40017     this.tpl = config.tpl || false;
40018     
40019     var el = config.el;
40020     var content = config.content;
40021
40022     if(config.autoCreate){ // xtype is available if this is called from factory
40023         el = Roo.id();
40024     }
40025     this.el = Roo.get(el);
40026     if(!this.el && config && config.autoCreate){
40027         if(typeof config.autoCreate == "object"){
40028             if(!config.autoCreate.id){
40029                 config.autoCreate.id = config.id||el;
40030             }
40031             this.el = Roo.DomHelper.append(document.body,
40032                         config.autoCreate, true);
40033         }else{
40034             var elcfg =  {
40035                 tag: "div",
40036                 cls: (config.cls || '') +
40037                     (config.background ? ' bg-' + config.background : '') +
40038                     " roo-layout-inactive-content",
40039                 id: config.id||el
40040             };
40041             if (config.iframe) {
40042                 elcfg.cn = [
40043                     {
40044                         tag : 'iframe',
40045                         style : 'border: 0px',
40046                         src : 'about:blank'
40047                     }
40048                 ];
40049             }
40050               
40051             if (config.html) {
40052                 elcfg.html = config.html;
40053                 
40054             }
40055                         
40056             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40057             if (config.iframe) {
40058                 this.iframeEl = this.el.select('iframe',true).first();
40059             }
40060             
40061         }
40062     } 
40063     this.closable = false;
40064     this.loaded = false;
40065     this.active = false;
40066    
40067       
40068     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40069         
40070         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40071         
40072         this.wrapEl = this.el; //this.el.wrap();
40073         var ti = [];
40074         if (config.toolbar.items) {
40075             ti = config.toolbar.items ;
40076             delete config.toolbar.items ;
40077         }
40078         
40079         var nitems = [];
40080         this.toolbar.render(this.wrapEl, 'before');
40081         for(var i =0;i < ti.length;i++) {
40082           //  Roo.log(['add child', items[i]]);
40083             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40084         }
40085         this.toolbar.items = nitems;
40086         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40087         delete config.toolbar;
40088         
40089     }
40090     /*
40091     // xtype created footer. - not sure if will work as we normally have to render first..
40092     if (this.footer && !this.footer.el && this.footer.xtype) {
40093         if (!this.wrapEl) {
40094             this.wrapEl = this.el.wrap();
40095         }
40096     
40097         this.footer.container = this.wrapEl.createChild();
40098          
40099         this.footer = Roo.factory(this.footer, Roo);
40100         
40101     }
40102     */
40103     
40104      if(typeof config == "string"){
40105         this.title = config;
40106     }else{
40107         Roo.apply(this, config);
40108     }
40109     
40110     if(this.resizeEl){
40111         this.resizeEl = Roo.get(this.resizeEl, true);
40112     }else{
40113         this.resizeEl = this.el;
40114     }
40115     // handle view.xtype
40116     
40117  
40118     
40119     
40120     this.addEvents({
40121         /**
40122          * @event activate
40123          * Fires when this panel is activated. 
40124          * @param {Roo.ContentPanel} this
40125          */
40126         "activate" : true,
40127         /**
40128          * @event deactivate
40129          * Fires when this panel is activated. 
40130          * @param {Roo.ContentPanel} this
40131          */
40132         "deactivate" : true,
40133
40134         /**
40135          * @event resize
40136          * Fires when this panel is resized if fitToFrame is true.
40137          * @param {Roo.ContentPanel} this
40138          * @param {Number} width The width after any component adjustments
40139          * @param {Number} height The height after any component adjustments
40140          */
40141         "resize" : true,
40142         
40143          /**
40144          * @event render
40145          * Fires when this tab is created
40146          * @param {Roo.ContentPanel} this
40147          */
40148         "render" : true
40149         
40150         
40151         
40152     });
40153     
40154
40155     
40156     
40157     if(this.autoScroll && !this.iframe){
40158         this.resizeEl.setStyle("overflow", "auto");
40159     } else {
40160         // fix randome scrolling
40161         //this.el.on('scroll', function() {
40162         //    Roo.log('fix random scolling');
40163         //    this.scrollTo('top',0); 
40164         //});
40165     }
40166     content = content || this.content;
40167     if(content){
40168         this.setContent(content);
40169     }
40170     if(config && config.url){
40171         this.setUrl(this.url, this.params, this.loadOnce);
40172     }
40173     
40174     
40175     
40176     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40177     
40178     if (this.view && typeof(this.view.xtype) != 'undefined') {
40179         this.view.el = this.el.appendChild(document.createElement("div"));
40180         this.view = Roo.factory(this.view); 
40181         this.view.render  &&  this.view.render(false, '');  
40182     }
40183     
40184     
40185     this.fireEvent('render', this);
40186 };
40187
40188 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40189     
40190     cls : '',
40191     background : '',
40192     
40193     tabTip : '',
40194     
40195     iframe : false,
40196     iframeEl : false,
40197     
40198     setRegion : function(region){
40199         this.region = region;
40200         this.setActiveClass(region && !this.background);
40201     },
40202     
40203     
40204     setActiveClass: function(state)
40205     {
40206         if(state){
40207            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40208            this.el.setStyle('position','relative');
40209         }else{
40210            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40211            this.el.setStyle('position', 'absolute');
40212         } 
40213     },
40214     
40215     /**
40216      * Returns the toolbar for this Panel if one was configured. 
40217      * @return {Roo.Toolbar} 
40218      */
40219     getToolbar : function(){
40220         return this.toolbar;
40221     },
40222     
40223     setActiveState : function(active)
40224     {
40225         this.active = active;
40226         this.setActiveClass(active);
40227         if(!active){
40228             if(this.fireEvent("deactivate", this) === false){
40229                 return false;
40230             }
40231             return true;
40232         }
40233         this.fireEvent("activate", this);
40234         return true;
40235     },
40236     /**
40237      * Updates this panel's element (not for iframe)
40238      * @param {String} content The new content
40239      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40240     */
40241     setContent : function(content, loadScripts){
40242         if (this.iframe) {
40243             return;
40244         }
40245         
40246         this.el.update(content, loadScripts);
40247     },
40248
40249     ignoreResize : function(w, h){
40250         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40251             return true;
40252         }else{
40253             this.lastSize = {width: w, height: h};
40254             return false;
40255         }
40256     },
40257     /**
40258      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40259      * @return {Roo.UpdateManager} The UpdateManager
40260      */
40261     getUpdateManager : function(){
40262         if (this.iframe) {
40263             return false;
40264         }
40265         return this.el.getUpdateManager();
40266     },
40267      /**
40268      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40269      * Does not work with IFRAME contents
40270      * @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:
40271 <pre><code>
40272 panel.load({
40273     url: "your-url.php",
40274     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40275     callback: yourFunction,
40276     scope: yourObject, //(optional scope)
40277     discardUrl: false,
40278     nocache: false,
40279     text: "Loading...",
40280     timeout: 30,
40281     scripts: false
40282 });
40283 </code></pre>
40284      
40285      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40286      * 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.
40287      * @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}
40288      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40289      * @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.
40290      * @return {Roo.ContentPanel} this
40291      */
40292     load : function(){
40293         
40294         if (this.iframe) {
40295             return this;
40296         }
40297         
40298         var um = this.el.getUpdateManager();
40299         um.update.apply(um, arguments);
40300         return this;
40301     },
40302
40303
40304     /**
40305      * 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.
40306      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40307      * @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)
40308      * @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)
40309      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40310      */
40311     setUrl : function(url, params, loadOnce){
40312         if (this.iframe) {
40313             this.iframeEl.dom.src = url;
40314             return false;
40315         }
40316         
40317         if(this.refreshDelegate){
40318             this.removeListener("activate", this.refreshDelegate);
40319         }
40320         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40321         this.on("activate", this.refreshDelegate);
40322         return this.el.getUpdateManager();
40323     },
40324     
40325     _handleRefresh : function(url, params, loadOnce){
40326         if(!loadOnce || !this.loaded){
40327             var updater = this.el.getUpdateManager();
40328             updater.update(url, params, this._setLoaded.createDelegate(this));
40329         }
40330     },
40331     
40332     _setLoaded : function(){
40333         this.loaded = true;
40334     }, 
40335     
40336     /**
40337      * Returns this panel's id
40338      * @return {String} 
40339      */
40340     getId : function(){
40341         return this.el.id;
40342     },
40343     
40344     /** 
40345      * Returns this panel's element - used by regiosn to add.
40346      * @return {Roo.Element} 
40347      */
40348     getEl : function(){
40349         return this.wrapEl || this.el;
40350     },
40351     
40352    
40353     
40354     adjustForComponents : function(width, height)
40355     {
40356         //Roo.log('adjustForComponents ');
40357         if(this.resizeEl != this.el){
40358             width -= this.el.getFrameWidth('lr');
40359             height -= this.el.getFrameWidth('tb');
40360         }
40361         if(this.toolbar){
40362             var te = this.toolbar.getEl();
40363             te.setWidth(width);
40364             height -= te.getHeight();
40365         }
40366         if(this.footer){
40367             var te = this.footer.getEl();
40368             te.setWidth(width);
40369             height -= te.getHeight();
40370         }
40371         
40372         
40373         if(this.adjustments){
40374             width += this.adjustments[0];
40375             height += this.adjustments[1];
40376         }
40377         return {"width": width, "height": height};
40378     },
40379     
40380     setSize : function(width, height){
40381         if(this.fitToFrame && !this.ignoreResize(width, height)){
40382             if(this.fitContainer && this.resizeEl != this.el){
40383                 this.el.setSize(width, height);
40384             }
40385             var size = this.adjustForComponents(width, height);
40386             if (this.iframe) {
40387                 this.iframeEl.setSize(width,height);
40388             }
40389             
40390             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40391             this.fireEvent('resize', this, size.width, size.height);
40392             
40393             
40394         }
40395     },
40396     
40397     /**
40398      * Returns this panel's title
40399      * @return {String} 
40400      */
40401     getTitle : function(){
40402         
40403         if (typeof(this.title) != 'object') {
40404             return this.title;
40405         }
40406         
40407         var t = '';
40408         for (var k in this.title) {
40409             if (!this.title.hasOwnProperty(k)) {
40410                 continue;
40411             }
40412             
40413             if (k.indexOf('-') >= 0) {
40414                 var s = k.split('-');
40415                 for (var i = 0; i<s.length; i++) {
40416                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40417                 }
40418             } else {
40419                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40420             }
40421         }
40422         return t;
40423     },
40424     
40425     /**
40426      * Set this panel's title
40427      * @param {String} title
40428      */
40429     setTitle : function(title){
40430         this.title = title;
40431         if(this.region){
40432             this.region.updatePanelTitle(this, title);
40433         }
40434     },
40435     
40436     /**
40437      * Returns true is this panel was configured to be closable
40438      * @return {Boolean} 
40439      */
40440     isClosable : function(){
40441         return this.closable;
40442     },
40443     
40444     beforeSlide : function(){
40445         this.el.clip();
40446         this.resizeEl.clip();
40447     },
40448     
40449     afterSlide : function(){
40450         this.el.unclip();
40451         this.resizeEl.unclip();
40452     },
40453     
40454     /**
40455      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40456      *   Will fail silently if the {@link #setUrl} method has not been called.
40457      *   This does not activate the panel, just updates its content.
40458      */
40459     refresh : function(){
40460         if(this.refreshDelegate){
40461            this.loaded = false;
40462            this.refreshDelegate();
40463         }
40464     },
40465     
40466     /**
40467      * Destroys this panel
40468      */
40469     destroy : function(){
40470         this.el.removeAllListeners();
40471         var tempEl = document.createElement("span");
40472         tempEl.appendChild(this.el.dom);
40473         tempEl.innerHTML = "";
40474         this.el.remove();
40475         this.el = null;
40476     },
40477     
40478     /**
40479      * form - if the content panel contains a form - this is a reference to it.
40480      * @type {Roo.form.Form}
40481      */
40482     form : false,
40483     /**
40484      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40485      *    This contains a reference to it.
40486      * @type {Roo.View}
40487      */
40488     view : false,
40489     
40490       /**
40491      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40492      * <pre><code>
40493
40494 layout.addxtype({
40495        xtype : 'Form',
40496        items: [ .... ]
40497    }
40498 );
40499
40500 </code></pre>
40501      * @param {Object} cfg Xtype definition of item to add.
40502      */
40503     
40504     
40505     getChildContainer: function () {
40506         return this.getEl();
40507     }
40508     
40509     
40510     /*
40511         var  ret = new Roo.factory(cfg);
40512         return ret;
40513         
40514         
40515         // add form..
40516         if (cfg.xtype.match(/^Form$/)) {
40517             
40518             var el;
40519             //if (this.footer) {
40520             //    el = this.footer.container.insertSibling(false, 'before');
40521             //} else {
40522                 el = this.el.createChild();
40523             //}
40524
40525             this.form = new  Roo.form.Form(cfg);
40526             
40527             
40528             if ( this.form.allItems.length) {
40529                 this.form.render(el.dom);
40530             }
40531             return this.form;
40532         }
40533         // should only have one of theses..
40534         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40535             // views.. should not be just added - used named prop 'view''
40536             
40537             cfg.el = this.el.appendChild(document.createElement("div"));
40538             // factory?
40539             
40540             var ret = new Roo.factory(cfg);
40541              
40542              ret.render && ret.render(false, ''); // render blank..
40543             this.view = ret;
40544             return ret;
40545         }
40546         return false;
40547     }
40548     \*/
40549 });
40550  
40551 /**
40552  * @class Roo.bootstrap.panel.Grid
40553  * @extends Roo.bootstrap.panel.Content
40554  * @constructor
40555  * Create a new GridPanel.
40556  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40557  * @param {Object} config A the config object
40558   
40559  */
40560
40561
40562
40563 Roo.bootstrap.panel.Grid = function(config)
40564 {
40565     
40566       
40567     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40568         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40569
40570     config.el = this.wrapper;
40571     //this.el = this.wrapper;
40572     
40573       if (config.container) {
40574         // ctor'ed from a Border/panel.grid
40575         
40576         
40577         this.wrapper.setStyle("overflow", "hidden");
40578         this.wrapper.addClass('roo-grid-container');
40579
40580     }
40581     
40582     
40583     if(config.toolbar){
40584         var tool_el = this.wrapper.createChild();    
40585         this.toolbar = Roo.factory(config.toolbar);
40586         var ti = [];
40587         if (config.toolbar.items) {
40588             ti = config.toolbar.items ;
40589             delete config.toolbar.items ;
40590         }
40591         
40592         var nitems = [];
40593         this.toolbar.render(tool_el);
40594         for(var i =0;i < ti.length;i++) {
40595           //  Roo.log(['add child', items[i]]);
40596             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40597         }
40598         this.toolbar.items = nitems;
40599         
40600         delete config.toolbar;
40601     }
40602     
40603     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40604     config.grid.scrollBody = true;;
40605     config.grid.monitorWindowResize = false; // turn off autosizing
40606     config.grid.autoHeight = false;
40607     config.grid.autoWidth = false;
40608     
40609     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40610     
40611     if (config.background) {
40612         // render grid on panel activation (if panel background)
40613         this.on('activate', function(gp) {
40614             if (!gp.grid.rendered) {
40615                 gp.grid.render(this.wrapper);
40616                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40617             }
40618         });
40619             
40620     } else {
40621         this.grid.render(this.wrapper);
40622         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40623
40624     }
40625     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40626     // ??? needed ??? config.el = this.wrapper;
40627     
40628     
40629     
40630   
40631     // xtype created footer. - not sure if will work as we normally have to render first..
40632     if (this.footer && !this.footer.el && this.footer.xtype) {
40633         
40634         var ctr = this.grid.getView().getFooterPanel(true);
40635         this.footer.dataSource = this.grid.dataSource;
40636         this.footer = Roo.factory(this.footer, Roo);
40637         this.footer.render(ctr);
40638         
40639     }
40640     
40641     
40642     
40643     
40644      
40645 };
40646
40647 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40648     getId : function(){
40649         return this.grid.id;
40650     },
40651     
40652     /**
40653      * Returns the grid for this panel
40654      * @return {Roo.bootstrap.Table} 
40655      */
40656     getGrid : function(){
40657         return this.grid;    
40658     },
40659     
40660     setSize : function(width, height){
40661         if(!this.ignoreResize(width, height)){
40662             var grid = this.grid;
40663             var size = this.adjustForComponents(width, height);
40664             // tfoot is not a footer?
40665           
40666             
40667             var gridel = grid.getGridEl();
40668             gridel.setSize(size.width, size.height);
40669             
40670             var tbd = grid.getGridEl().select('tbody', true).first();
40671             var thd = grid.getGridEl().select('thead',true).first();
40672             var tbf= grid.getGridEl().select('tfoot', true).first();
40673
40674             if (tbf) {
40675                 size.height -= tbf.getHeight();
40676             }
40677             if (thd) {
40678                 size.height -= thd.getHeight();
40679             }
40680             
40681             tbd.setSize(size.width, size.height );
40682             // this is for the account management tab -seems to work there.
40683             var thd = grid.getGridEl().select('thead',true).first();
40684             //if (tbd) {
40685             //    tbd.setSize(size.width, size.height - thd.getHeight());
40686             //}
40687              
40688             grid.autoSize();
40689         }
40690     },
40691      
40692     
40693     
40694     beforeSlide : function(){
40695         this.grid.getView().scroller.clip();
40696     },
40697     
40698     afterSlide : function(){
40699         this.grid.getView().scroller.unclip();
40700     },
40701     
40702     destroy : function(){
40703         this.grid.destroy();
40704         delete this.grid;
40705         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40706     }
40707 });
40708
40709 /**
40710  * @class Roo.bootstrap.panel.Nest
40711  * @extends Roo.bootstrap.panel.Content
40712  * @constructor
40713  * Create a new Panel, that can contain a layout.Border.
40714  * 
40715  * 
40716  * @param {Roo.BorderLayout} layout The layout for this panel
40717  * @param {String/Object} config A string to set only the title or a config object
40718  */
40719 Roo.bootstrap.panel.Nest = function(config)
40720 {
40721     // construct with only one argument..
40722     /* FIXME - implement nicer consturctors
40723     if (layout.layout) {
40724         config = layout;
40725         layout = config.layout;
40726         delete config.layout;
40727     }
40728     if (layout.xtype && !layout.getEl) {
40729         // then layout needs constructing..
40730         layout = Roo.factory(layout, Roo);
40731     }
40732     */
40733     
40734     config.el =  config.layout.getEl();
40735     
40736     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40737     
40738     config.layout.monitorWindowResize = false; // turn off autosizing
40739     this.layout = config.layout;
40740     this.layout.getEl().addClass("roo-layout-nested-layout");
40741     this.layout.parent = this;
40742     
40743     
40744     
40745     
40746 };
40747
40748 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40749
40750     setSize : function(width, height){
40751         if(!this.ignoreResize(width, height)){
40752             var size = this.adjustForComponents(width, height);
40753             var el = this.layout.getEl();
40754             if (size.height < 1) {
40755                 el.setWidth(size.width);   
40756             } else {
40757                 el.setSize(size.width, size.height);
40758             }
40759             var touch = el.dom.offsetWidth;
40760             this.layout.layout();
40761             // ie requires a double layout on the first pass
40762             if(Roo.isIE && !this.initialized){
40763                 this.initialized = true;
40764                 this.layout.layout();
40765             }
40766         }
40767     },
40768     
40769     // activate all subpanels if not currently active..
40770     
40771     setActiveState : function(active){
40772         this.active = active;
40773         this.setActiveClass(active);
40774         
40775         if(!active){
40776             this.fireEvent("deactivate", this);
40777             return;
40778         }
40779         
40780         this.fireEvent("activate", this);
40781         // not sure if this should happen before or after..
40782         if (!this.layout) {
40783             return; // should not happen..
40784         }
40785         var reg = false;
40786         for (var r in this.layout.regions) {
40787             reg = this.layout.getRegion(r);
40788             if (reg.getActivePanel()) {
40789                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40790                 reg.setActivePanel(reg.getActivePanel());
40791                 continue;
40792             }
40793             if (!reg.panels.length) {
40794                 continue;
40795             }
40796             reg.showPanel(reg.getPanel(0));
40797         }
40798         
40799         
40800         
40801         
40802     },
40803     
40804     /**
40805      * Returns the nested BorderLayout for this panel
40806      * @return {Roo.BorderLayout} 
40807      */
40808     getLayout : function(){
40809         return this.layout;
40810     },
40811     
40812      /**
40813      * Adds a xtype elements to the layout of the nested panel
40814      * <pre><code>
40815
40816 panel.addxtype({
40817        xtype : 'ContentPanel',
40818        region: 'west',
40819        items: [ .... ]
40820    }
40821 );
40822
40823 panel.addxtype({
40824         xtype : 'NestedLayoutPanel',
40825         region: 'west',
40826         layout: {
40827            center: { },
40828            west: { }   
40829         },
40830         items : [ ... list of content panels or nested layout panels.. ]
40831    }
40832 );
40833 </code></pre>
40834      * @param {Object} cfg Xtype definition of item to add.
40835      */
40836     addxtype : function(cfg) {
40837         return this.layout.addxtype(cfg);
40838     
40839     }
40840 });/*
40841  * Based on:
40842  * Ext JS Library 1.1.1
40843  * Copyright(c) 2006-2007, Ext JS, LLC.
40844  *
40845  * Originally Released Under LGPL - original licence link has changed is not relivant.
40846  *
40847  * Fork - LGPL
40848  * <script type="text/javascript">
40849  */
40850 /**
40851  * @class Roo.TabPanel
40852  * @extends Roo.util.Observable
40853  * A lightweight tab container.
40854  * <br><br>
40855  * Usage:
40856  * <pre><code>
40857 // basic tabs 1, built from existing content
40858 var tabs = new Roo.TabPanel("tabs1");
40859 tabs.addTab("script", "View Script");
40860 tabs.addTab("markup", "View Markup");
40861 tabs.activate("script");
40862
40863 // more advanced tabs, built from javascript
40864 var jtabs = new Roo.TabPanel("jtabs");
40865 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40866
40867 // set up the UpdateManager
40868 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40869 var updater = tab2.getUpdateManager();
40870 updater.setDefaultUrl("ajax1.htm");
40871 tab2.on('activate', updater.refresh, updater, true);
40872
40873 // Use setUrl for Ajax loading
40874 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40875 tab3.setUrl("ajax2.htm", null, true);
40876
40877 // Disabled tab
40878 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40879 tab4.disable();
40880
40881 jtabs.activate("jtabs-1");
40882  * </code></pre>
40883  * @constructor
40884  * Create a new TabPanel.
40885  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40886  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40887  */
40888 Roo.bootstrap.panel.Tabs = function(config){
40889     /**
40890     * The container element for this TabPanel.
40891     * @type Roo.Element
40892     */
40893     this.el = Roo.get(config.el);
40894     delete config.el;
40895     if(config){
40896         if(typeof config == "boolean"){
40897             this.tabPosition = config ? "bottom" : "top";
40898         }else{
40899             Roo.apply(this, config);
40900         }
40901     }
40902     
40903     if(this.tabPosition == "bottom"){
40904         // if tabs are at the bottom = create the body first.
40905         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40906         this.el.addClass("roo-tabs-bottom");
40907     }
40908     // next create the tabs holders
40909     
40910     if (this.tabPosition == "west"){
40911         
40912         var reg = this.region; // fake it..
40913         while (reg) {
40914             if (!reg.mgr.parent) {
40915                 break;
40916             }
40917             reg = reg.mgr.parent.region;
40918         }
40919         Roo.log("got nest?");
40920         Roo.log(reg);
40921         if (reg.mgr.getRegion('west')) {
40922             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40923             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40924             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40925             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40926             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40927         
40928             
40929         }
40930         
40931         
40932     } else {
40933      
40934         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40935         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40936         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40937         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40938     }
40939     
40940     
40941     if(Roo.isIE){
40942         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40943     }
40944     
40945     // finally - if tabs are at the top, then create the body last..
40946     if(this.tabPosition != "bottom"){
40947         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40948          * @type Roo.Element
40949          */
40950         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40951         this.el.addClass("roo-tabs-top");
40952     }
40953     this.items = [];
40954
40955     this.bodyEl.setStyle("position", "relative");
40956
40957     this.active = null;
40958     this.activateDelegate = this.activate.createDelegate(this);
40959
40960     this.addEvents({
40961         /**
40962          * @event tabchange
40963          * Fires when the active tab changes
40964          * @param {Roo.TabPanel} this
40965          * @param {Roo.TabPanelItem} activePanel The new active tab
40966          */
40967         "tabchange": true,
40968         /**
40969          * @event beforetabchange
40970          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40971          * @param {Roo.TabPanel} this
40972          * @param {Object} e Set cancel to true on this object to cancel the tab change
40973          * @param {Roo.TabPanelItem} tab The tab being changed to
40974          */
40975         "beforetabchange" : true
40976     });
40977
40978     Roo.EventManager.onWindowResize(this.onResize, this);
40979     this.cpad = this.el.getPadding("lr");
40980     this.hiddenCount = 0;
40981
40982
40983     // toolbar on the tabbar support...
40984     if (this.toolbar) {
40985         alert("no toolbar support yet");
40986         this.toolbar  = false;
40987         /*
40988         var tcfg = this.toolbar;
40989         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40990         this.toolbar = new Roo.Toolbar(tcfg);
40991         if (Roo.isSafari) {
40992             var tbl = tcfg.container.child('table', true);
40993             tbl.setAttribute('width', '100%');
40994         }
40995         */
40996         
40997     }
40998    
40999
41000
41001     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41002 };
41003
41004 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41005     /*
41006      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41007      */
41008     tabPosition : "top",
41009     /*
41010      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41011      */
41012     currentTabWidth : 0,
41013     /*
41014      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41015      */
41016     minTabWidth : 40,
41017     /*
41018      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41019      */
41020     maxTabWidth : 250,
41021     /*
41022      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41023      */
41024     preferredTabWidth : 175,
41025     /*
41026      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41027      */
41028     resizeTabs : false,
41029     /*
41030      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41031      */
41032     monitorResize : true,
41033     /*
41034      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41035      */
41036     toolbar : false,  // set by caller..
41037     
41038     region : false, /// set by caller
41039     
41040     disableTooltips : true, // not used yet...
41041
41042     /**
41043      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41044      * @param {String} id The id of the div to use <b>or create</b>
41045      * @param {String} text The text for the tab
41046      * @param {String} content (optional) Content to put in the TabPanelItem body
41047      * @param {Boolean} closable (optional) True to create a close icon on the tab
41048      * @return {Roo.TabPanelItem} The created TabPanelItem
41049      */
41050     addTab : function(id, text, content, closable, tpl)
41051     {
41052         var item = new Roo.bootstrap.panel.TabItem({
41053             panel: this,
41054             id : id,
41055             text : text,
41056             closable : closable,
41057             tpl : tpl
41058         });
41059         this.addTabItem(item);
41060         if(content){
41061             item.setContent(content);
41062         }
41063         return item;
41064     },
41065
41066     /**
41067      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41068      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41069      * @return {Roo.TabPanelItem}
41070      */
41071     getTab : function(id){
41072         return this.items[id];
41073     },
41074
41075     /**
41076      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41077      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41078      */
41079     hideTab : function(id){
41080         var t = this.items[id];
41081         if(!t.isHidden()){
41082            t.setHidden(true);
41083            this.hiddenCount++;
41084            this.autoSizeTabs();
41085         }
41086     },
41087
41088     /**
41089      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41090      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41091      */
41092     unhideTab : function(id){
41093         var t = this.items[id];
41094         if(t.isHidden()){
41095            t.setHidden(false);
41096            this.hiddenCount--;
41097            this.autoSizeTabs();
41098         }
41099     },
41100
41101     /**
41102      * Adds an existing {@link Roo.TabPanelItem}.
41103      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41104      */
41105     addTabItem : function(item)
41106     {
41107         this.items[item.id] = item;
41108         this.items.push(item);
41109         this.autoSizeTabs();
41110       //  if(this.resizeTabs){
41111     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41112   //         this.autoSizeTabs();
41113 //        }else{
41114 //            item.autoSize();
41115        // }
41116     },
41117
41118     /**
41119      * Removes a {@link Roo.TabPanelItem}.
41120      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41121      */
41122     removeTab : function(id){
41123         var items = this.items;
41124         var tab = items[id];
41125         if(!tab) { return; }
41126         var index = items.indexOf(tab);
41127         if(this.active == tab && items.length > 1){
41128             var newTab = this.getNextAvailable(index);
41129             if(newTab) {
41130                 newTab.activate();
41131             }
41132         }
41133         this.stripEl.dom.removeChild(tab.pnode.dom);
41134         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41135             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41136         }
41137         items.splice(index, 1);
41138         delete this.items[tab.id];
41139         tab.fireEvent("close", tab);
41140         tab.purgeListeners();
41141         this.autoSizeTabs();
41142     },
41143
41144     getNextAvailable : function(start){
41145         var items = this.items;
41146         var index = start;
41147         // look for a next tab that will slide over to
41148         // replace the one being removed
41149         while(index < items.length){
41150             var item = items[++index];
41151             if(item && !item.isHidden()){
41152                 return item;
41153             }
41154         }
41155         // if one isn't found select the previous tab (on the left)
41156         index = start;
41157         while(index >= 0){
41158             var item = items[--index];
41159             if(item && !item.isHidden()){
41160                 return item;
41161             }
41162         }
41163         return null;
41164     },
41165
41166     /**
41167      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41168      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41169      */
41170     disableTab : function(id){
41171         var tab = this.items[id];
41172         if(tab && this.active != tab){
41173             tab.disable();
41174         }
41175     },
41176
41177     /**
41178      * Enables a {@link Roo.TabPanelItem} that is disabled.
41179      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41180      */
41181     enableTab : function(id){
41182         var tab = this.items[id];
41183         tab.enable();
41184     },
41185
41186     /**
41187      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41188      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41189      * @return {Roo.TabPanelItem} The TabPanelItem.
41190      */
41191     activate : function(id)
41192     {
41193         //Roo.log('activite:'  + id);
41194         
41195         var tab = this.items[id];
41196         if(!tab){
41197             return null;
41198         }
41199         if(tab == this.active || tab.disabled){
41200             return tab;
41201         }
41202         var e = {};
41203         this.fireEvent("beforetabchange", this, e, tab);
41204         if(e.cancel !== true && !tab.disabled){
41205             if(this.active){
41206                 this.active.hide();
41207             }
41208             this.active = this.items[id];
41209             this.active.show();
41210             this.fireEvent("tabchange", this, this.active);
41211         }
41212         return tab;
41213     },
41214
41215     /**
41216      * Gets the active {@link Roo.TabPanelItem}.
41217      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41218      */
41219     getActiveTab : function(){
41220         return this.active;
41221     },
41222
41223     /**
41224      * Updates the tab body element to fit the height of the container element
41225      * for overflow scrolling
41226      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41227      */
41228     syncHeight : function(targetHeight){
41229         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41230         var bm = this.bodyEl.getMargins();
41231         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41232         this.bodyEl.setHeight(newHeight);
41233         return newHeight;
41234     },
41235
41236     onResize : function(){
41237         if(this.monitorResize){
41238             this.autoSizeTabs();
41239         }
41240     },
41241
41242     /**
41243      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41244      */
41245     beginUpdate : function(){
41246         this.updating = true;
41247     },
41248
41249     /**
41250      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41251      */
41252     endUpdate : function(){
41253         this.updating = false;
41254         this.autoSizeTabs();
41255     },
41256
41257     /**
41258      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41259      */
41260     autoSizeTabs : function()
41261     {
41262         var count = this.items.length;
41263         var vcount = count - this.hiddenCount;
41264         
41265         if (vcount < 2) {
41266             this.stripEl.hide();
41267         } else {
41268             this.stripEl.show();
41269         }
41270         
41271         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41272             return;
41273         }
41274         
41275         
41276         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41277         var availWidth = Math.floor(w / vcount);
41278         var b = this.stripBody;
41279         if(b.getWidth() > w){
41280             var tabs = this.items;
41281             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41282             if(availWidth < this.minTabWidth){
41283                 /*if(!this.sleft){    // incomplete scrolling code
41284                     this.createScrollButtons();
41285                 }
41286                 this.showScroll();
41287                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41288             }
41289         }else{
41290             if(this.currentTabWidth < this.preferredTabWidth){
41291                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41292             }
41293         }
41294     },
41295
41296     /**
41297      * Returns the number of tabs in this TabPanel.
41298      * @return {Number}
41299      */
41300      getCount : function(){
41301          return this.items.length;
41302      },
41303
41304     /**
41305      * Resizes all the tabs to the passed width
41306      * @param {Number} The new width
41307      */
41308     setTabWidth : function(width){
41309         this.currentTabWidth = width;
41310         for(var i = 0, len = this.items.length; i < len; i++) {
41311                 if(!this.items[i].isHidden()) {
41312                 this.items[i].setWidth(width);
41313             }
41314         }
41315     },
41316
41317     /**
41318      * Destroys this TabPanel
41319      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41320      */
41321     destroy : function(removeEl){
41322         Roo.EventManager.removeResizeListener(this.onResize, this);
41323         for(var i = 0, len = this.items.length; i < len; i++){
41324             this.items[i].purgeListeners();
41325         }
41326         if(removeEl === true){
41327             this.el.update("");
41328             this.el.remove();
41329         }
41330     },
41331     
41332     createStrip : function(container)
41333     {
41334         var strip = document.createElement("nav");
41335         strip.className = Roo.bootstrap.version == 4 ?
41336             "navbar-light bg-light" : 
41337             "navbar navbar-default"; //"x-tabs-wrap";
41338         container.appendChild(strip);
41339         return strip;
41340     },
41341     
41342     createStripList : function(strip)
41343     {
41344         // div wrapper for retard IE
41345         // returns the "tr" element.
41346         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41347         //'<div class="x-tabs-strip-wrap">'+
41348           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41349           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41350         return strip.firstChild; //.firstChild.firstChild.firstChild;
41351     },
41352     createBody : function(container)
41353     {
41354         var body = document.createElement("div");
41355         Roo.id(body, "tab-body");
41356         //Roo.fly(body).addClass("x-tabs-body");
41357         Roo.fly(body).addClass("tab-content");
41358         container.appendChild(body);
41359         return body;
41360     },
41361     createItemBody :function(bodyEl, id){
41362         var body = Roo.getDom(id);
41363         if(!body){
41364             body = document.createElement("div");
41365             body.id = id;
41366         }
41367         //Roo.fly(body).addClass("x-tabs-item-body");
41368         Roo.fly(body).addClass("tab-pane");
41369          bodyEl.insertBefore(body, bodyEl.firstChild);
41370         return body;
41371     },
41372     /** @private */
41373     createStripElements :  function(stripEl, text, closable, tpl)
41374     {
41375         var td = document.createElement("li"); // was td..
41376         td.className = 'nav-item';
41377         
41378         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41379         
41380         
41381         stripEl.appendChild(td);
41382         /*if(closable){
41383             td.className = "x-tabs-closable";
41384             if(!this.closeTpl){
41385                 this.closeTpl = new Roo.Template(
41386                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41387                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41388                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41389                 );
41390             }
41391             var el = this.closeTpl.overwrite(td, {"text": text});
41392             var close = el.getElementsByTagName("div")[0];
41393             var inner = el.getElementsByTagName("em")[0];
41394             return {"el": el, "close": close, "inner": inner};
41395         } else {
41396         */
41397         // not sure what this is..
41398 //            if(!this.tabTpl){
41399                 //this.tabTpl = new Roo.Template(
41400                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41401                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41402                 //);
41403 //                this.tabTpl = new Roo.Template(
41404 //                   '<a href="#">' +
41405 //                   '<span unselectable="on"' +
41406 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41407 //                            ' >{text}</span></a>'
41408 //                );
41409 //                
41410 //            }
41411
41412
41413             var template = tpl || this.tabTpl || false;
41414             
41415             if(!template){
41416                 template =  new Roo.Template(
41417                         Roo.bootstrap.version == 4 ? 
41418                             (
41419                                 '<a class="nav-link" href="#" unselectable="on"' +
41420                                      (this.disableTooltips ? '' : ' title="{text}"') +
41421                                      ' >{text}</a>'
41422                             ) : (
41423                                 '<a class="nav-link" href="#">' +
41424                                 '<span unselectable="on"' +
41425                                          (this.disableTooltips ? '' : ' title="{text}"') +
41426                                     ' >{text}</span></a>'
41427                             )
41428                 );
41429             }
41430             
41431             switch (typeof(template)) {
41432                 case 'object' :
41433                     break;
41434                 case 'string' :
41435                     template = new Roo.Template(template);
41436                     break;
41437                 default :
41438                     break;
41439             }
41440             
41441             var el = template.overwrite(td, {"text": text});
41442             
41443             var inner = el.getElementsByTagName("span")[0];
41444             
41445             return {"el": el, "inner": inner};
41446             
41447     }
41448         
41449     
41450 });
41451
41452 /**
41453  * @class Roo.TabPanelItem
41454  * @extends Roo.util.Observable
41455  * Represents an individual item (tab plus body) in a TabPanel.
41456  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41457  * @param {String} id The id of this TabPanelItem
41458  * @param {String} text The text for the tab of this TabPanelItem
41459  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41460  */
41461 Roo.bootstrap.panel.TabItem = function(config){
41462     /**
41463      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41464      * @type Roo.TabPanel
41465      */
41466     this.tabPanel = config.panel;
41467     /**
41468      * The id for this TabPanelItem
41469      * @type String
41470      */
41471     this.id = config.id;
41472     /** @private */
41473     this.disabled = false;
41474     /** @private */
41475     this.text = config.text;
41476     /** @private */
41477     this.loaded = false;
41478     this.closable = config.closable;
41479
41480     /**
41481      * The body element for this TabPanelItem.
41482      * @type Roo.Element
41483      */
41484     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41485     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41486     this.bodyEl.setStyle("display", "block");
41487     this.bodyEl.setStyle("zoom", "1");
41488     //this.hideAction();
41489
41490     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41491     /** @private */
41492     this.el = Roo.get(els.el);
41493     this.inner = Roo.get(els.inner, true);
41494      this.textEl = Roo.bootstrap.version == 4 ?
41495         this.el : Roo.get(this.el.dom.firstChild, true);
41496
41497     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41498     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41499
41500     
41501 //    this.el.on("mousedown", this.onTabMouseDown, this);
41502     this.el.on("click", this.onTabClick, this);
41503     /** @private */
41504     if(config.closable){
41505         var c = Roo.get(els.close, true);
41506         c.dom.title = this.closeText;
41507         c.addClassOnOver("close-over");
41508         c.on("click", this.closeClick, this);
41509      }
41510
41511     this.addEvents({
41512          /**
41513          * @event activate
41514          * Fires when this tab becomes the active tab.
41515          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41516          * @param {Roo.TabPanelItem} this
41517          */
41518         "activate": true,
41519         /**
41520          * @event beforeclose
41521          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41522          * @param {Roo.TabPanelItem} this
41523          * @param {Object} e Set cancel to true on this object to cancel the close.
41524          */
41525         "beforeclose": true,
41526         /**
41527          * @event close
41528          * Fires when this tab is closed.
41529          * @param {Roo.TabPanelItem} this
41530          */
41531          "close": true,
41532         /**
41533          * @event deactivate
41534          * Fires when this tab is no longer the active tab.
41535          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41536          * @param {Roo.TabPanelItem} this
41537          */
41538          "deactivate" : true
41539     });
41540     this.hidden = false;
41541
41542     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41543 };
41544
41545 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41546            {
41547     purgeListeners : function(){
41548        Roo.util.Observable.prototype.purgeListeners.call(this);
41549        this.el.removeAllListeners();
41550     },
41551     /**
41552      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41553      */
41554     show : function(){
41555         this.status_node.addClass("active");
41556         this.showAction();
41557         if(Roo.isOpera){
41558             this.tabPanel.stripWrap.repaint();
41559         }
41560         this.fireEvent("activate", this.tabPanel, this);
41561     },
41562
41563     /**
41564      * Returns true if this tab is the active tab.
41565      * @return {Boolean}
41566      */
41567     isActive : function(){
41568         return this.tabPanel.getActiveTab() == this;
41569     },
41570
41571     /**
41572      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41573      */
41574     hide : function(){
41575         this.status_node.removeClass("active");
41576         this.hideAction();
41577         this.fireEvent("deactivate", this.tabPanel, this);
41578     },
41579
41580     hideAction : function(){
41581         this.bodyEl.hide();
41582         this.bodyEl.setStyle("position", "absolute");
41583         this.bodyEl.setLeft("-20000px");
41584         this.bodyEl.setTop("-20000px");
41585     },
41586
41587     showAction : function(){
41588         this.bodyEl.setStyle("position", "relative");
41589         this.bodyEl.setTop("");
41590         this.bodyEl.setLeft("");
41591         this.bodyEl.show();
41592     },
41593
41594     /**
41595      * Set the tooltip for the tab.
41596      * @param {String} tooltip The tab's tooltip
41597      */
41598     setTooltip : function(text){
41599         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41600             this.textEl.dom.qtip = text;
41601             this.textEl.dom.removeAttribute('title');
41602         }else{
41603             this.textEl.dom.title = text;
41604         }
41605     },
41606
41607     onTabClick : function(e){
41608         e.preventDefault();
41609         this.tabPanel.activate(this.id);
41610     },
41611
41612     onTabMouseDown : function(e){
41613         e.preventDefault();
41614         this.tabPanel.activate(this.id);
41615     },
41616 /*
41617     getWidth : function(){
41618         return this.inner.getWidth();
41619     },
41620
41621     setWidth : function(width){
41622         var iwidth = width - this.linode.getPadding("lr");
41623         this.inner.setWidth(iwidth);
41624         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41625         this.linode.setWidth(width);
41626     },
41627 */
41628     /**
41629      * Show or hide the tab
41630      * @param {Boolean} hidden True to hide or false to show.
41631      */
41632     setHidden : function(hidden){
41633         this.hidden = hidden;
41634         this.linode.setStyle("display", hidden ? "none" : "");
41635     },
41636
41637     /**
41638      * Returns true if this tab is "hidden"
41639      * @return {Boolean}
41640      */
41641     isHidden : function(){
41642         return this.hidden;
41643     },
41644
41645     /**
41646      * Returns the text for this tab
41647      * @return {String}
41648      */
41649     getText : function(){
41650         return this.text;
41651     },
41652     /*
41653     autoSize : function(){
41654         //this.el.beginMeasure();
41655         this.textEl.setWidth(1);
41656         /*
41657          *  #2804 [new] Tabs in Roojs
41658          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41659          */
41660         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41661         //this.el.endMeasure();
41662     //},
41663
41664     /**
41665      * Sets the text for the tab (Note: this also sets the tooltip text)
41666      * @param {String} text The tab's text and tooltip
41667      */
41668     setText : function(text){
41669         this.text = text;
41670         this.textEl.update(text);
41671         this.setTooltip(text);
41672         //if(!this.tabPanel.resizeTabs){
41673         //    this.autoSize();
41674         //}
41675     },
41676     /**
41677      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41678      */
41679     activate : function(){
41680         this.tabPanel.activate(this.id);
41681     },
41682
41683     /**
41684      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41685      */
41686     disable : function(){
41687         if(this.tabPanel.active != this){
41688             this.disabled = true;
41689             this.status_node.addClass("disabled");
41690         }
41691     },
41692
41693     /**
41694      * Enables this TabPanelItem if it was previously disabled.
41695      */
41696     enable : function(){
41697         this.disabled = false;
41698         this.status_node.removeClass("disabled");
41699     },
41700
41701     /**
41702      * Sets the content for this TabPanelItem.
41703      * @param {String} content The content
41704      * @param {Boolean} loadScripts true to look for and load scripts
41705      */
41706     setContent : function(content, loadScripts){
41707         this.bodyEl.update(content, loadScripts);
41708     },
41709
41710     /**
41711      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41712      * @return {Roo.UpdateManager} The UpdateManager
41713      */
41714     getUpdateManager : function(){
41715         return this.bodyEl.getUpdateManager();
41716     },
41717
41718     /**
41719      * Set a URL to be used to load the content for this TabPanelItem.
41720      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41721      * @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)
41722      * @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)
41723      * @return {Roo.UpdateManager} The UpdateManager
41724      */
41725     setUrl : function(url, params, loadOnce){
41726         if(this.refreshDelegate){
41727             this.un('activate', this.refreshDelegate);
41728         }
41729         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41730         this.on("activate", this.refreshDelegate);
41731         return this.bodyEl.getUpdateManager();
41732     },
41733
41734     /** @private */
41735     _handleRefresh : function(url, params, loadOnce){
41736         if(!loadOnce || !this.loaded){
41737             var updater = this.bodyEl.getUpdateManager();
41738             updater.update(url, params, this._setLoaded.createDelegate(this));
41739         }
41740     },
41741
41742     /**
41743      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41744      *   Will fail silently if the setUrl method has not been called.
41745      *   This does not activate the panel, just updates its content.
41746      */
41747     refresh : function(){
41748         if(this.refreshDelegate){
41749            this.loaded = false;
41750            this.refreshDelegate();
41751         }
41752     },
41753
41754     /** @private */
41755     _setLoaded : function(){
41756         this.loaded = true;
41757     },
41758
41759     /** @private */
41760     closeClick : function(e){
41761         var o = {};
41762         e.stopEvent();
41763         this.fireEvent("beforeclose", this, o);
41764         if(o.cancel !== true){
41765             this.tabPanel.removeTab(this.id);
41766         }
41767     },
41768     /**
41769      * The text displayed in the tooltip for the close icon.
41770      * @type String
41771      */
41772     closeText : "Close this tab"
41773 });
41774 /**
41775 *    This script refer to:
41776 *    Title: International Telephone Input
41777 *    Author: Jack O'Connor
41778 *    Code version:  v12.1.12
41779 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41780 **/
41781
41782 Roo.bootstrap.PhoneInputData = function() {
41783     var d = [
41784       [
41785         "Afghanistan (‫افغانستان‬‎)",
41786         "af",
41787         "93"
41788       ],
41789       [
41790         "Albania (Shqipëri)",
41791         "al",
41792         "355"
41793       ],
41794       [
41795         "Algeria (‫الجزائر‬‎)",
41796         "dz",
41797         "213"
41798       ],
41799       [
41800         "American Samoa",
41801         "as",
41802         "1684"
41803       ],
41804       [
41805         "Andorra",
41806         "ad",
41807         "376"
41808       ],
41809       [
41810         "Angola",
41811         "ao",
41812         "244"
41813       ],
41814       [
41815         "Anguilla",
41816         "ai",
41817         "1264"
41818       ],
41819       [
41820         "Antigua and Barbuda",
41821         "ag",
41822         "1268"
41823       ],
41824       [
41825         "Argentina",
41826         "ar",
41827         "54"
41828       ],
41829       [
41830         "Armenia (Հայաստան)",
41831         "am",
41832         "374"
41833       ],
41834       [
41835         "Aruba",
41836         "aw",
41837         "297"
41838       ],
41839       [
41840         "Australia",
41841         "au",
41842         "61",
41843         0
41844       ],
41845       [
41846         "Austria (Österreich)",
41847         "at",
41848         "43"
41849       ],
41850       [
41851         "Azerbaijan (Azərbaycan)",
41852         "az",
41853         "994"
41854       ],
41855       [
41856         "Bahamas",
41857         "bs",
41858         "1242"
41859       ],
41860       [
41861         "Bahrain (‫البحرين‬‎)",
41862         "bh",
41863         "973"
41864       ],
41865       [
41866         "Bangladesh (বাংলাদেশ)",
41867         "bd",
41868         "880"
41869       ],
41870       [
41871         "Barbados",
41872         "bb",
41873         "1246"
41874       ],
41875       [
41876         "Belarus (Беларусь)",
41877         "by",
41878         "375"
41879       ],
41880       [
41881         "Belgium (België)",
41882         "be",
41883         "32"
41884       ],
41885       [
41886         "Belize",
41887         "bz",
41888         "501"
41889       ],
41890       [
41891         "Benin (Bénin)",
41892         "bj",
41893         "229"
41894       ],
41895       [
41896         "Bermuda",
41897         "bm",
41898         "1441"
41899       ],
41900       [
41901         "Bhutan (འབྲུག)",
41902         "bt",
41903         "975"
41904       ],
41905       [
41906         "Bolivia",
41907         "bo",
41908         "591"
41909       ],
41910       [
41911         "Bosnia and Herzegovina (Босна и Херцеговина)",
41912         "ba",
41913         "387"
41914       ],
41915       [
41916         "Botswana",
41917         "bw",
41918         "267"
41919       ],
41920       [
41921         "Brazil (Brasil)",
41922         "br",
41923         "55"
41924       ],
41925       [
41926         "British Indian Ocean Territory",
41927         "io",
41928         "246"
41929       ],
41930       [
41931         "British Virgin Islands",
41932         "vg",
41933         "1284"
41934       ],
41935       [
41936         "Brunei",
41937         "bn",
41938         "673"
41939       ],
41940       [
41941         "Bulgaria (България)",
41942         "bg",
41943         "359"
41944       ],
41945       [
41946         "Burkina Faso",
41947         "bf",
41948         "226"
41949       ],
41950       [
41951         "Burundi (Uburundi)",
41952         "bi",
41953         "257"
41954       ],
41955       [
41956         "Cambodia (កម្ពុជា)",
41957         "kh",
41958         "855"
41959       ],
41960       [
41961         "Cameroon (Cameroun)",
41962         "cm",
41963         "237"
41964       ],
41965       [
41966         "Canada",
41967         "ca",
41968         "1",
41969         1,
41970         ["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"]
41971       ],
41972       [
41973         "Cape Verde (Kabu Verdi)",
41974         "cv",
41975         "238"
41976       ],
41977       [
41978         "Caribbean Netherlands",
41979         "bq",
41980         "599",
41981         1
41982       ],
41983       [
41984         "Cayman Islands",
41985         "ky",
41986         "1345"
41987       ],
41988       [
41989         "Central African Republic (République centrafricaine)",
41990         "cf",
41991         "236"
41992       ],
41993       [
41994         "Chad (Tchad)",
41995         "td",
41996         "235"
41997       ],
41998       [
41999         "Chile",
42000         "cl",
42001         "56"
42002       ],
42003       [
42004         "China (中国)",
42005         "cn",
42006         "86"
42007       ],
42008       [
42009         "Christmas Island",
42010         "cx",
42011         "61",
42012         2
42013       ],
42014       [
42015         "Cocos (Keeling) Islands",
42016         "cc",
42017         "61",
42018         1
42019       ],
42020       [
42021         "Colombia",
42022         "co",
42023         "57"
42024       ],
42025       [
42026         "Comoros (‫جزر القمر‬‎)",
42027         "km",
42028         "269"
42029       ],
42030       [
42031         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42032         "cd",
42033         "243"
42034       ],
42035       [
42036         "Congo (Republic) (Congo-Brazzaville)",
42037         "cg",
42038         "242"
42039       ],
42040       [
42041         "Cook Islands",
42042         "ck",
42043         "682"
42044       ],
42045       [
42046         "Costa Rica",
42047         "cr",
42048         "506"
42049       ],
42050       [
42051         "Côte d’Ivoire",
42052         "ci",
42053         "225"
42054       ],
42055       [
42056         "Croatia (Hrvatska)",
42057         "hr",
42058         "385"
42059       ],
42060       [
42061         "Cuba",
42062         "cu",
42063         "53"
42064       ],
42065       [
42066         "Curaçao",
42067         "cw",
42068         "599",
42069         0
42070       ],
42071       [
42072         "Cyprus (Κύπρος)",
42073         "cy",
42074         "357"
42075       ],
42076       [
42077         "Czech Republic (Česká republika)",
42078         "cz",
42079         "420"
42080       ],
42081       [
42082         "Denmark (Danmark)",
42083         "dk",
42084         "45"
42085       ],
42086       [
42087         "Djibouti",
42088         "dj",
42089         "253"
42090       ],
42091       [
42092         "Dominica",
42093         "dm",
42094         "1767"
42095       ],
42096       [
42097         "Dominican Republic (República Dominicana)",
42098         "do",
42099         "1",
42100         2,
42101         ["809", "829", "849"]
42102       ],
42103       [
42104         "Ecuador",
42105         "ec",
42106         "593"
42107       ],
42108       [
42109         "Egypt (‫مصر‬‎)",
42110         "eg",
42111         "20"
42112       ],
42113       [
42114         "El Salvador",
42115         "sv",
42116         "503"
42117       ],
42118       [
42119         "Equatorial Guinea (Guinea Ecuatorial)",
42120         "gq",
42121         "240"
42122       ],
42123       [
42124         "Eritrea",
42125         "er",
42126         "291"
42127       ],
42128       [
42129         "Estonia (Eesti)",
42130         "ee",
42131         "372"
42132       ],
42133       [
42134         "Ethiopia",
42135         "et",
42136         "251"
42137       ],
42138       [
42139         "Falkland Islands (Islas Malvinas)",
42140         "fk",
42141         "500"
42142       ],
42143       [
42144         "Faroe Islands (Føroyar)",
42145         "fo",
42146         "298"
42147       ],
42148       [
42149         "Fiji",
42150         "fj",
42151         "679"
42152       ],
42153       [
42154         "Finland (Suomi)",
42155         "fi",
42156         "358",
42157         0
42158       ],
42159       [
42160         "France",
42161         "fr",
42162         "33"
42163       ],
42164       [
42165         "French Guiana (Guyane française)",
42166         "gf",
42167         "594"
42168       ],
42169       [
42170         "French Polynesia (Polynésie française)",
42171         "pf",
42172         "689"
42173       ],
42174       [
42175         "Gabon",
42176         "ga",
42177         "241"
42178       ],
42179       [
42180         "Gambia",
42181         "gm",
42182         "220"
42183       ],
42184       [
42185         "Georgia (საქართველო)",
42186         "ge",
42187         "995"
42188       ],
42189       [
42190         "Germany (Deutschland)",
42191         "de",
42192         "49"
42193       ],
42194       [
42195         "Ghana (Gaana)",
42196         "gh",
42197         "233"
42198       ],
42199       [
42200         "Gibraltar",
42201         "gi",
42202         "350"
42203       ],
42204       [
42205         "Greece (Ελλάδα)",
42206         "gr",
42207         "30"
42208       ],
42209       [
42210         "Greenland (Kalaallit Nunaat)",
42211         "gl",
42212         "299"
42213       ],
42214       [
42215         "Grenada",
42216         "gd",
42217         "1473"
42218       ],
42219       [
42220         "Guadeloupe",
42221         "gp",
42222         "590",
42223         0
42224       ],
42225       [
42226         "Guam",
42227         "gu",
42228         "1671"
42229       ],
42230       [
42231         "Guatemala",
42232         "gt",
42233         "502"
42234       ],
42235       [
42236         "Guernsey",
42237         "gg",
42238         "44",
42239         1
42240       ],
42241       [
42242         "Guinea (Guinée)",
42243         "gn",
42244         "224"
42245       ],
42246       [
42247         "Guinea-Bissau (Guiné Bissau)",
42248         "gw",
42249         "245"
42250       ],
42251       [
42252         "Guyana",
42253         "gy",
42254         "592"
42255       ],
42256       [
42257         "Haiti",
42258         "ht",
42259         "509"
42260       ],
42261       [
42262         "Honduras",
42263         "hn",
42264         "504"
42265       ],
42266       [
42267         "Hong Kong (香港)",
42268         "hk",
42269         "852"
42270       ],
42271       [
42272         "Hungary (Magyarország)",
42273         "hu",
42274         "36"
42275       ],
42276       [
42277         "Iceland (Ísland)",
42278         "is",
42279         "354"
42280       ],
42281       [
42282         "India (भारत)",
42283         "in",
42284         "91"
42285       ],
42286       [
42287         "Indonesia",
42288         "id",
42289         "62"
42290       ],
42291       [
42292         "Iran (‫ایران‬‎)",
42293         "ir",
42294         "98"
42295       ],
42296       [
42297         "Iraq (‫العراق‬‎)",
42298         "iq",
42299         "964"
42300       ],
42301       [
42302         "Ireland",
42303         "ie",
42304         "353"
42305       ],
42306       [
42307         "Isle of Man",
42308         "im",
42309         "44",
42310         2
42311       ],
42312       [
42313         "Israel (‫ישראל‬‎)",
42314         "il",
42315         "972"
42316       ],
42317       [
42318         "Italy (Italia)",
42319         "it",
42320         "39",
42321         0
42322       ],
42323       [
42324         "Jamaica",
42325         "jm",
42326         "1876"
42327       ],
42328       [
42329         "Japan (日本)",
42330         "jp",
42331         "81"
42332       ],
42333       [
42334         "Jersey",
42335         "je",
42336         "44",
42337         3
42338       ],
42339       [
42340         "Jordan (‫الأردن‬‎)",
42341         "jo",
42342         "962"
42343       ],
42344       [
42345         "Kazakhstan (Казахстан)",
42346         "kz",
42347         "7",
42348         1
42349       ],
42350       [
42351         "Kenya",
42352         "ke",
42353         "254"
42354       ],
42355       [
42356         "Kiribati",
42357         "ki",
42358         "686"
42359       ],
42360       [
42361         "Kosovo",
42362         "xk",
42363         "383"
42364       ],
42365       [
42366         "Kuwait (‫الكويت‬‎)",
42367         "kw",
42368         "965"
42369       ],
42370       [
42371         "Kyrgyzstan (Кыргызстан)",
42372         "kg",
42373         "996"
42374       ],
42375       [
42376         "Laos (ລາວ)",
42377         "la",
42378         "856"
42379       ],
42380       [
42381         "Latvia (Latvija)",
42382         "lv",
42383         "371"
42384       ],
42385       [
42386         "Lebanon (‫لبنان‬‎)",
42387         "lb",
42388         "961"
42389       ],
42390       [
42391         "Lesotho",
42392         "ls",
42393         "266"
42394       ],
42395       [
42396         "Liberia",
42397         "lr",
42398         "231"
42399       ],
42400       [
42401         "Libya (‫ليبيا‬‎)",
42402         "ly",
42403         "218"
42404       ],
42405       [
42406         "Liechtenstein",
42407         "li",
42408         "423"
42409       ],
42410       [
42411         "Lithuania (Lietuva)",
42412         "lt",
42413         "370"
42414       ],
42415       [
42416         "Luxembourg",
42417         "lu",
42418         "352"
42419       ],
42420       [
42421         "Macau (澳門)",
42422         "mo",
42423         "853"
42424       ],
42425       [
42426         "Macedonia (FYROM) (Македонија)",
42427         "mk",
42428         "389"
42429       ],
42430       [
42431         "Madagascar (Madagasikara)",
42432         "mg",
42433         "261"
42434       ],
42435       [
42436         "Malawi",
42437         "mw",
42438         "265"
42439       ],
42440       [
42441         "Malaysia",
42442         "my",
42443         "60"
42444       ],
42445       [
42446         "Maldives",
42447         "mv",
42448         "960"
42449       ],
42450       [
42451         "Mali",
42452         "ml",
42453         "223"
42454       ],
42455       [
42456         "Malta",
42457         "mt",
42458         "356"
42459       ],
42460       [
42461         "Marshall Islands",
42462         "mh",
42463         "692"
42464       ],
42465       [
42466         "Martinique",
42467         "mq",
42468         "596"
42469       ],
42470       [
42471         "Mauritania (‫موريتانيا‬‎)",
42472         "mr",
42473         "222"
42474       ],
42475       [
42476         "Mauritius (Moris)",
42477         "mu",
42478         "230"
42479       ],
42480       [
42481         "Mayotte",
42482         "yt",
42483         "262",
42484         1
42485       ],
42486       [
42487         "Mexico (México)",
42488         "mx",
42489         "52"
42490       ],
42491       [
42492         "Micronesia",
42493         "fm",
42494         "691"
42495       ],
42496       [
42497         "Moldova (Republica Moldova)",
42498         "md",
42499         "373"
42500       ],
42501       [
42502         "Monaco",
42503         "mc",
42504         "377"
42505       ],
42506       [
42507         "Mongolia (Монгол)",
42508         "mn",
42509         "976"
42510       ],
42511       [
42512         "Montenegro (Crna Gora)",
42513         "me",
42514         "382"
42515       ],
42516       [
42517         "Montserrat",
42518         "ms",
42519         "1664"
42520       ],
42521       [
42522         "Morocco (‫المغرب‬‎)",
42523         "ma",
42524         "212",
42525         0
42526       ],
42527       [
42528         "Mozambique (Moçambique)",
42529         "mz",
42530         "258"
42531       ],
42532       [
42533         "Myanmar (Burma) (မြန်မာ)",
42534         "mm",
42535         "95"
42536       ],
42537       [
42538         "Namibia (Namibië)",
42539         "na",
42540         "264"
42541       ],
42542       [
42543         "Nauru",
42544         "nr",
42545         "674"
42546       ],
42547       [
42548         "Nepal (नेपाल)",
42549         "np",
42550         "977"
42551       ],
42552       [
42553         "Netherlands (Nederland)",
42554         "nl",
42555         "31"
42556       ],
42557       [
42558         "New Caledonia (Nouvelle-Calédonie)",
42559         "nc",
42560         "687"
42561       ],
42562       [
42563         "New Zealand",
42564         "nz",
42565         "64"
42566       ],
42567       [
42568         "Nicaragua",
42569         "ni",
42570         "505"
42571       ],
42572       [
42573         "Niger (Nijar)",
42574         "ne",
42575         "227"
42576       ],
42577       [
42578         "Nigeria",
42579         "ng",
42580         "234"
42581       ],
42582       [
42583         "Niue",
42584         "nu",
42585         "683"
42586       ],
42587       [
42588         "Norfolk Island",
42589         "nf",
42590         "672"
42591       ],
42592       [
42593         "North Korea (조선 민주주의 인민 공화국)",
42594         "kp",
42595         "850"
42596       ],
42597       [
42598         "Northern Mariana Islands",
42599         "mp",
42600         "1670"
42601       ],
42602       [
42603         "Norway (Norge)",
42604         "no",
42605         "47",
42606         0
42607       ],
42608       [
42609         "Oman (‫عُمان‬‎)",
42610         "om",
42611         "968"
42612       ],
42613       [
42614         "Pakistan (‫پاکستان‬‎)",
42615         "pk",
42616         "92"
42617       ],
42618       [
42619         "Palau",
42620         "pw",
42621         "680"
42622       ],
42623       [
42624         "Palestine (‫فلسطين‬‎)",
42625         "ps",
42626         "970"
42627       ],
42628       [
42629         "Panama (Panamá)",
42630         "pa",
42631         "507"
42632       ],
42633       [
42634         "Papua New Guinea",
42635         "pg",
42636         "675"
42637       ],
42638       [
42639         "Paraguay",
42640         "py",
42641         "595"
42642       ],
42643       [
42644         "Peru (Perú)",
42645         "pe",
42646         "51"
42647       ],
42648       [
42649         "Philippines",
42650         "ph",
42651         "63"
42652       ],
42653       [
42654         "Poland (Polska)",
42655         "pl",
42656         "48"
42657       ],
42658       [
42659         "Portugal",
42660         "pt",
42661         "351"
42662       ],
42663       [
42664         "Puerto Rico",
42665         "pr",
42666         "1",
42667         3,
42668         ["787", "939"]
42669       ],
42670       [
42671         "Qatar (‫قطر‬‎)",
42672         "qa",
42673         "974"
42674       ],
42675       [
42676         "Réunion (La Réunion)",
42677         "re",
42678         "262",
42679         0
42680       ],
42681       [
42682         "Romania (România)",
42683         "ro",
42684         "40"
42685       ],
42686       [
42687         "Russia (Россия)",
42688         "ru",
42689         "7",
42690         0
42691       ],
42692       [
42693         "Rwanda",
42694         "rw",
42695         "250"
42696       ],
42697       [
42698         "Saint Barthélemy",
42699         "bl",
42700         "590",
42701         1
42702       ],
42703       [
42704         "Saint Helena",
42705         "sh",
42706         "290"
42707       ],
42708       [
42709         "Saint Kitts and Nevis",
42710         "kn",
42711         "1869"
42712       ],
42713       [
42714         "Saint Lucia",
42715         "lc",
42716         "1758"
42717       ],
42718       [
42719         "Saint Martin (Saint-Martin (partie française))",
42720         "mf",
42721         "590",
42722         2
42723       ],
42724       [
42725         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42726         "pm",
42727         "508"
42728       ],
42729       [
42730         "Saint Vincent and the Grenadines",
42731         "vc",
42732         "1784"
42733       ],
42734       [
42735         "Samoa",
42736         "ws",
42737         "685"
42738       ],
42739       [
42740         "San Marino",
42741         "sm",
42742         "378"
42743       ],
42744       [
42745         "São Tomé and Príncipe (São Tomé e Príncipe)",
42746         "st",
42747         "239"
42748       ],
42749       [
42750         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42751         "sa",
42752         "966"
42753       ],
42754       [
42755         "Senegal (Sénégal)",
42756         "sn",
42757         "221"
42758       ],
42759       [
42760         "Serbia (Србија)",
42761         "rs",
42762         "381"
42763       ],
42764       [
42765         "Seychelles",
42766         "sc",
42767         "248"
42768       ],
42769       [
42770         "Sierra Leone",
42771         "sl",
42772         "232"
42773       ],
42774       [
42775         "Singapore",
42776         "sg",
42777         "65"
42778       ],
42779       [
42780         "Sint Maarten",
42781         "sx",
42782         "1721"
42783       ],
42784       [
42785         "Slovakia (Slovensko)",
42786         "sk",
42787         "421"
42788       ],
42789       [
42790         "Slovenia (Slovenija)",
42791         "si",
42792         "386"
42793       ],
42794       [
42795         "Solomon Islands",
42796         "sb",
42797         "677"
42798       ],
42799       [
42800         "Somalia (Soomaaliya)",
42801         "so",
42802         "252"
42803       ],
42804       [
42805         "South Africa",
42806         "za",
42807         "27"
42808       ],
42809       [
42810         "South Korea (대한민국)",
42811         "kr",
42812         "82"
42813       ],
42814       [
42815         "South Sudan (‫جنوب السودان‬‎)",
42816         "ss",
42817         "211"
42818       ],
42819       [
42820         "Spain (España)",
42821         "es",
42822         "34"
42823       ],
42824       [
42825         "Sri Lanka (ශ්‍රී ලංකාව)",
42826         "lk",
42827         "94"
42828       ],
42829       [
42830         "Sudan (‫السودان‬‎)",
42831         "sd",
42832         "249"
42833       ],
42834       [
42835         "Suriname",
42836         "sr",
42837         "597"
42838       ],
42839       [
42840         "Svalbard and Jan Mayen",
42841         "sj",
42842         "47",
42843         1
42844       ],
42845       [
42846         "Swaziland",
42847         "sz",
42848         "268"
42849       ],
42850       [
42851         "Sweden (Sverige)",
42852         "se",
42853         "46"
42854       ],
42855       [
42856         "Switzerland (Schweiz)",
42857         "ch",
42858         "41"
42859       ],
42860       [
42861         "Syria (‫سوريا‬‎)",
42862         "sy",
42863         "963"
42864       ],
42865       [
42866         "Taiwan (台灣)",
42867         "tw",
42868         "886"
42869       ],
42870       [
42871         "Tajikistan",
42872         "tj",
42873         "992"
42874       ],
42875       [
42876         "Tanzania",
42877         "tz",
42878         "255"
42879       ],
42880       [
42881         "Thailand (ไทย)",
42882         "th",
42883         "66"
42884       ],
42885       [
42886         "Timor-Leste",
42887         "tl",
42888         "670"
42889       ],
42890       [
42891         "Togo",
42892         "tg",
42893         "228"
42894       ],
42895       [
42896         "Tokelau",
42897         "tk",
42898         "690"
42899       ],
42900       [
42901         "Tonga",
42902         "to",
42903         "676"
42904       ],
42905       [
42906         "Trinidad and Tobago",
42907         "tt",
42908         "1868"
42909       ],
42910       [
42911         "Tunisia (‫تونس‬‎)",
42912         "tn",
42913         "216"
42914       ],
42915       [
42916         "Turkey (Türkiye)",
42917         "tr",
42918         "90"
42919       ],
42920       [
42921         "Turkmenistan",
42922         "tm",
42923         "993"
42924       ],
42925       [
42926         "Turks and Caicos Islands",
42927         "tc",
42928         "1649"
42929       ],
42930       [
42931         "Tuvalu",
42932         "tv",
42933         "688"
42934       ],
42935       [
42936         "U.S. Virgin Islands",
42937         "vi",
42938         "1340"
42939       ],
42940       [
42941         "Uganda",
42942         "ug",
42943         "256"
42944       ],
42945       [
42946         "Ukraine (Україна)",
42947         "ua",
42948         "380"
42949       ],
42950       [
42951         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42952         "ae",
42953         "971"
42954       ],
42955       [
42956         "United Kingdom",
42957         "gb",
42958         "44",
42959         0
42960       ],
42961       [
42962         "United States",
42963         "us",
42964         "1",
42965         0
42966       ],
42967       [
42968         "Uruguay",
42969         "uy",
42970         "598"
42971       ],
42972       [
42973         "Uzbekistan (Oʻzbekiston)",
42974         "uz",
42975         "998"
42976       ],
42977       [
42978         "Vanuatu",
42979         "vu",
42980         "678"
42981       ],
42982       [
42983         "Vatican City (Città del Vaticano)",
42984         "va",
42985         "39",
42986         1
42987       ],
42988       [
42989         "Venezuela",
42990         "ve",
42991         "58"
42992       ],
42993       [
42994         "Vietnam (Việt Nam)",
42995         "vn",
42996         "84"
42997       ],
42998       [
42999         "Wallis and Futuna (Wallis-et-Futuna)",
43000         "wf",
43001         "681"
43002       ],
43003       [
43004         "Western Sahara (‫الصحراء الغربية‬‎)",
43005         "eh",
43006         "212",
43007         1
43008       ],
43009       [
43010         "Yemen (‫اليمن‬‎)",
43011         "ye",
43012         "967"
43013       ],
43014       [
43015         "Zambia",
43016         "zm",
43017         "260"
43018       ],
43019       [
43020         "Zimbabwe",
43021         "zw",
43022         "263"
43023       ],
43024       [
43025         "Åland Islands",
43026         "ax",
43027         "358",
43028         1
43029       ]
43030   ];
43031   
43032   return d;
43033 }/**
43034 *    This script refer to:
43035 *    Title: International Telephone Input
43036 *    Author: Jack O'Connor
43037 *    Code version:  v12.1.12
43038 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43039 **/
43040
43041 /**
43042  * @class Roo.bootstrap.PhoneInput
43043  * @extends Roo.bootstrap.TriggerField
43044  * An input with International dial-code selection
43045  
43046  * @cfg {String} defaultDialCode default '+852'
43047  * @cfg {Array} preferedCountries default []
43048   
43049  * @constructor
43050  * Create a new PhoneInput.
43051  * @param {Object} config Configuration options
43052  */
43053
43054 Roo.bootstrap.PhoneInput = function(config) {
43055     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43056 };
43057
43058 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43059         
43060         listWidth: undefined,
43061         
43062         selectedClass: 'active',
43063         
43064         invalidClass : "has-warning",
43065         
43066         validClass: 'has-success',
43067         
43068         allowed: '0123456789',
43069         
43070         max_length: 15,
43071         
43072         /**
43073          * @cfg {String} defaultDialCode The default dial code when initializing the input
43074          */
43075         defaultDialCode: '+852',
43076         
43077         /**
43078          * @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
43079          */
43080         preferedCountries: false,
43081         
43082         getAutoCreate : function()
43083         {
43084             var data = Roo.bootstrap.PhoneInputData();
43085             var align = this.labelAlign || this.parentLabelAlign();
43086             var id = Roo.id();
43087             
43088             this.allCountries = [];
43089             this.dialCodeMapping = [];
43090             
43091             for (var i = 0; i < data.length; i++) {
43092               var c = data[i];
43093               this.allCountries[i] = {
43094                 name: c[0],
43095                 iso2: c[1],
43096                 dialCode: c[2],
43097                 priority: c[3] || 0,
43098                 areaCodes: c[4] || null
43099               };
43100               this.dialCodeMapping[c[2]] = {
43101                   name: c[0],
43102                   iso2: c[1],
43103                   priority: c[3] || 0,
43104                   areaCodes: c[4] || null
43105               };
43106             }
43107             
43108             var cfg = {
43109                 cls: 'form-group',
43110                 cn: []
43111             };
43112             
43113             var input =  {
43114                 tag: 'input',
43115                 id : id,
43116                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43117                 maxlength: this.max_length,
43118                 cls : 'form-control tel-input',
43119                 autocomplete: 'new-password'
43120             };
43121             
43122             var hiddenInput = {
43123                 tag: 'input',
43124                 type: 'hidden',
43125                 cls: 'hidden-tel-input'
43126             };
43127             
43128             if (this.name) {
43129                 hiddenInput.name = this.name;
43130             }
43131             
43132             if (this.disabled) {
43133                 input.disabled = true;
43134             }
43135             
43136             var flag_container = {
43137                 tag: 'div',
43138                 cls: 'flag-box',
43139                 cn: [
43140                     {
43141                         tag: 'div',
43142                         cls: 'flag'
43143                     },
43144                     {
43145                         tag: 'div',
43146                         cls: 'caret'
43147                     }
43148                 ]
43149             };
43150             
43151             var box = {
43152                 tag: 'div',
43153                 cls: this.hasFeedback ? 'has-feedback' : '',
43154                 cn: [
43155                     hiddenInput,
43156                     input,
43157                     {
43158                         tag: 'input',
43159                         cls: 'dial-code-holder',
43160                         disabled: true
43161                     }
43162                 ]
43163             };
43164             
43165             var container = {
43166                 cls: 'roo-select2-container input-group',
43167                 cn: [
43168                     flag_container,
43169                     box
43170                 ]
43171             };
43172             
43173             if (this.fieldLabel.length) {
43174                 var indicator = {
43175                     tag: 'i',
43176                     tooltip: 'This field is required'
43177                 };
43178                 
43179                 var label = {
43180                     tag: 'label',
43181                     'for':  id,
43182                     cls: 'control-label',
43183                     cn: []
43184                 };
43185                 
43186                 var label_text = {
43187                     tag: 'span',
43188                     html: this.fieldLabel
43189                 };
43190                 
43191                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43192                 label.cn = [
43193                     indicator,
43194                     label_text
43195                 ];
43196                 
43197                 if(this.indicatorpos == 'right') {
43198                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43199                     label.cn = [
43200                         label_text,
43201                         indicator
43202                     ];
43203                 }
43204                 
43205                 if(align == 'left') {
43206                     container = {
43207                         tag: 'div',
43208                         cn: [
43209                             container
43210                         ]
43211                     };
43212                     
43213                     if(this.labelWidth > 12){
43214                         label.style = "width: " + this.labelWidth + 'px';
43215                     }
43216                     if(this.labelWidth < 13 && this.labelmd == 0){
43217                         this.labelmd = this.labelWidth;
43218                     }
43219                     if(this.labellg > 0){
43220                         label.cls += ' col-lg-' + this.labellg;
43221                         input.cls += ' col-lg-' + (12 - this.labellg);
43222                     }
43223                     if(this.labelmd > 0){
43224                         label.cls += ' col-md-' + this.labelmd;
43225                         container.cls += ' col-md-' + (12 - this.labelmd);
43226                     }
43227                     if(this.labelsm > 0){
43228                         label.cls += ' col-sm-' + this.labelsm;
43229                         container.cls += ' col-sm-' + (12 - this.labelsm);
43230                     }
43231                     if(this.labelxs > 0){
43232                         label.cls += ' col-xs-' + this.labelxs;
43233                         container.cls += ' col-xs-' + (12 - this.labelxs);
43234                     }
43235                 }
43236             }
43237             
43238             cfg.cn = [
43239                 label,
43240                 container
43241             ];
43242             
43243             var settings = this;
43244             
43245             ['xs','sm','md','lg'].map(function(size){
43246                 if (settings[size]) {
43247                     cfg.cls += ' col-' + size + '-' + settings[size];
43248                 }
43249             });
43250             
43251             this.store = new Roo.data.Store({
43252                 proxy : new Roo.data.MemoryProxy({}),
43253                 reader : new Roo.data.JsonReader({
43254                     fields : [
43255                         {
43256                             'name' : 'name',
43257                             'type' : 'string'
43258                         },
43259                         {
43260                             'name' : 'iso2',
43261                             'type' : 'string'
43262                         },
43263                         {
43264                             'name' : 'dialCode',
43265                             'type' : 'string'
43266                         },
43267                         {
43268                             'name' : 'priority',
43269                             'type' : 'string'
43270                         },
43271                         {
43272                             'name' : 'areaCodes',
43273                             'type' : 'string'
43274                         }
43275                     ]
43276                 })
43277             });
43278             
43279             if(!this.preferedCountries) {
43280                 this.preferedCountries = [
43281                     'hk',
43282                     'gb',
43283                     'us'
43284                 ];
43285             }
43286             
43287             var p = this.preferedCountries.reverse();
43288             
43289             if(p) {
43290                 for (var i = 0; i < p.length; i++) {
43291                     for (var j = 0; j < this.allCountries.length; j++) {
43292                         if(this.allCountries[j].iso2 == p[i]) {
43293                             var t = this.allCountries[j];
43294                             this.allCountries.splice(j,1);
43295                             this.allCountries.unshift(t);
43296                         }
43297                     } 
43298                 }
43299             }
43300             
43301             this.store.proxy.data = {
43302                 success: true,
43303                 data: this.allCountries
43304             };
43305             
43306             return cfg;
43307         },
43308         
43309         initEvents : function()
43310         {
43311             this.createList();
43312             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43313             
43314             this.indicator = this.indicatorEl();
43315             this.flag = this.flagEl();
43316             this.dialCodeHolder = this.dialCodeHolderEl();
43317             
43318             this.trigger = this.el.select('div.flag-box',true).first();
43319             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43320             
43321             var _this = this;
43322             
43323             (function(){
43324                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43325                 _this.list.setWidth(lw);
43326             }).defer(100);
43327             
43328             this.list.on('mouseover', this.onViewOver, this);
43329             this.list.on('mousemove', this.onViewMove, this);
43330             this.inputEl().on("keyup", this.onKeyUp, this);
43331             this.inputEl().on("keypress", this.onKeyPress, this);
43332             
43333             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43334
43335             this.view = new Roo.View(this.list, this.tpl, {
43336                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43337             });
43338             
43339             this.view.on('click', this.onViewClick, this);
43340             this.setValue(this.defaultDialCode);
43341         },
43342         
43343         onTriggerClick : function(e)
43344         {
43345             Roo.log('trigger click');
43346             if(this.disabled){
43347                 return;
43348             }
43349             
43350             if(this.isExpanded()){
43351                 this.collapse();
43352                 this.hasFocus = false;
43353             }else {
43354                 this.store.load({});
43355                 this.hasFocus = true;
43356                 this.expand();
43357             }
43358         },
43359         
43360         isExpanded : function()
43361         {
43362             return this.list.isVisible();
43363         },
43364         
43365         collapse : function()
43366         {
43367             if(!this.isExpanded()){
43368                 return;
43369             }
43370             this.list.hide();
43371             Roo.get(document).un('mousedown', this.collapseIf, this);
43372             Roo.get(document).un('mousewheel', this.collapseIf, this);
43373             this.fireEvent('collapse', this);
43374             this.validate();
43375         },
43376         
43377         expand : function()
43378         {
43379             Roo.log('expand');
43380
43381             if(this.isExpanded() || !this.hasFocus){
43382                 return;
43383             }
43384             
43385             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43386             this.list.setWidth(lw);
43387             
43388             this.list.show();
43389             this.restrictHeight();
43390             
43391             Roo.get(document).on('mousedown', this.collapseIf, this);
43392             Roo.get(document).on('mousewheel', this.collapseIf, this);
43393             
43394             this.fireEvent('expand', this);
43395         },
43396         
43397         restrictHeight : function()
43398         {
43399             this.list.alignTo(this.inputEl(), this.listAlign);
43400             this.list.alignTo(this.inputEl(), this.listAlign);
43401         },
43402         
43403         onViewOver : function(e, t)
43404         {
43405             if(this.inKeyMode){
43406                 return;
43407             }
43408             var item = this.view.findItemFromChild(t);
43409             
43410             if(item){
43411                 var index = this.view.indexOf(item);
43412                 this.select(index, false);
43413             }
43414         },
43415
43416         // private
43417         onViewClick : function(view, doFocus, el, e)
43418         {
43419             var index = this.view.getSelectedIndexes()[0];
43420             
43421             var r = this.store.getAt(index);
43422             
43423             if(r){
43424                 this.onSelect(r, index);
43425             }
43426             if(doFocus !== false && !this.blockFocus){
43427                 this.inputEl().focus();
43428             }
43429         },
43430         
43431         onViewMove : function(e, t)
43432         {
43433             this.inKeyMode = false;
43434         },
43435         
43436         select : function(index, scrollIntoView)
43437         {
43438             this.selectedIndex = index;
43439             this.view.select(index);
43440             if(scrollIntoView !== false){
43441                 var el = this.view.getNode(index);
43442                 if(el){
43443                     this.list.scrollChildIntoView(el, false);
43444                 }
43445             }
43446         },
43447         
43448         createList : function()
43449         {
43450             this.list = Roo.get(document.body).createChild({
43451                 tag: 'ul',
43452                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43453                 style: 'display:none'
43454             });
43455             
43456             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43457         },
43458         
43459         collapseIf : function(e)
43460         {
43461             var in_combo  = e.within(this.el);
43462             var in_list =  e.within(this.list);
43463             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43464             
43465             if (in_combo || in_list || is_list) {
43466                 return;
43467             }
43468             this.collapse();
43469         },
43470         
43471         onSelect : function(record, index)
43472         {
43473             if(this.fireEvent('beforeselect', this, record, index) !== false){
43474                 
43475                 this.setFlagClass(record.data.iso2);
43476                 this.setDialCode(record.data.dialCode);
43477                 this.hasFocus = false;
43478                 this.collapse();
43479                 this.fireEvent('select', this, record, index);
43480             }
43481         },
43482         
43483         flagEl : function()
43484         {
43485             var flag = this.el.select('div.flag',true).first();
43486             if(!flag){
43487                 return false;
43488             }
43489             return flag;
43490         },
43491         
43492         dialCodeHolderEl : function()
43493         {
43494             var d = this.el.select('input.dial-code-holder',true).first();
43495             if(!d){
43496                 return false;
43497             }
43498             return d;
43499         },
43500         
43501         setDialCode : function(v)
43502         {
43503             this.dialCodeHolder.dom.value = '+'+v;
43504         },
43505         
43506         setFlagClass : function(n)
43507         {
43508             this.flag.dom.className = 'flag '+n;
43509         },
43510         
43511         getValue : function()
43512         {
43513             var v = this.inputEl().getValue();
43514             if(this.dialCodeHolder) {
43515                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43516             }
43517             return v;
43518         },
43519         
43520         setValue : function(v)
43521         {
43522             var d = this.getDialCode(v);
43523             
43524             //invalid dial code
43525             if(v.length == 0 || !d || d.length == 0) {
43526                 if(this.rendered){
43527                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43528                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43529                 }
43530                 return;
43531             }
43532             
43533             //valid dial code
43534             this.setFlagClass(this.dialCodeMapping[d].iso2);
43535             this.setDialCode(d);
43536             this.inputEl().dom.value = v.replace('+'+d,'');
43537             this.hiddenEl().dom.value = this.getValue();
43538             
43539             this.validate();
43540         },
43541         
43542         getDialCode : function(v)
43543         {
43544             v = v ||  '';
43545             
43546             if (v.length == 0) {
43547                 return this.dialCodeHolder.dom.value;
43548             }
43549             
43550             var dialCode = "";
43551             if (v.charAt(0) != "+") {
43552                 return false;
43553             }
43554             var numericChars = "";
43555             for (var i = 1; i < v.length; i++) {
43556               var c = v.charAt(i);
43557               if (!isNaN(c)) {
43558                 numericChars += c;
43559                 if (this.dialCodeMapping[numericChars]) {
43560                   dialCode = v.substr(1, i);
43561                 }
43562                 if (numericChars.length == 4) {
43563                   break;
43564                 }
43565               }
43566             }
43567             return dialCode;
43568         },
43569         
43570         reset : function()
43571         {
43572             this.setValue(this.defaultDialCode);
43573             this.validate();
43574         },
43575         
43576         hiddenEl : function()
43577         {
43578             return this.el.select('input.hidden-tel-input',true).first();
43579         },
43580         
43581         // after setting val
43582         onKeyUp : function(e){
43583             this.setValue(this.getValue());
43584         },
43585         
43586         onKeyPress : function(e){
43587             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43588                 e.stopEvent();
43589             }
43590         }
43591         
43592 });
43593 /**
43594  * @class Roo.bootstrap.MoneyField
43595  * @extends Roo.bootstrap.ComboBox
43596  * Bootstrap MoneyField class
43597  * 
43598  * @constructor
43599  * Create a new MoneyField.
43600  * @param {Object} config Configuration options
43601  */
43602
43603 Roo.bootstrap.MoneyField = function(config) {
43604     
43605     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43606     
43607 };
43608
43609 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43610     
43611     /**
43612      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43613      */
43614     allowDecimals : true,
43615     /**
43616      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43617      */
43618     decimalSeparator : ".",
43619     /**
43620      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43621      */
43622     decimalPrecision : 0,
43623     /**
43624      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43625      */
43626     allowNegative : true,
43627     /**
43628      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43629      */
43630     allowZero: true,
43631     /**
43632      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43633      */
43634     minValue : Number.NEGATIVE_INFINITY,
43635     /**
43636      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43637      */
43638     maxValue : Number.MAX_VALUE,
43639     /**
43640      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43641      */
43642     minText : "The minimum value for this field is {0}",
43643     /**
43644      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43645      */
43646     maxText : "The maximum value for this field is {0}",
43647     /**
43648      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43649      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43650      */
43651     nanText : "{0} is not a valid number",
43652     /**
43653      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43654      */
43655     castInt : true,
43656     /**
43657      * @cfg {String} defaults currency of the MoneyField
43658      * value should be in lkey
43659      */
43660     defaultCurrency : false,
43661     /**
43662      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43663      */
43664     thousandsDelimiter : false,
43665     /**
43666      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43667      */
43668     max_length: false,
43669     
43670     inputlg : 9,
43671     inputmd : 9,
43672     inputsm : 9,
43673     inputxs : 6,
43674     
43675     store : false,
43676     
43677     getAutoCreate : function()
43678     {
43679         var align = this.labelAlign || this.parentLabelAlign();
43680         
43681         var id = Roo.id();
43682
43683         var cfg = {
43684             cls: 'form-group',
43685             cn: []
43686         };
43687
43688         var input =  {
43689             tag: 'input',
43690             id : id,
43691             cls : 'form-control roo-money-amount-input',
43692             autocomplete: 'new-password'
43693         };
43694         
43695         var hiddenInput = {
43696             tag: 'input',
43697             type: 'hidden',
43698             id: Roo.id(),
43699             cls: 'hidden-number-input'
43700         };
43701         
43702         if(this.max_length) {
43703             input.maxlength = this.max_length; 
43704         }
43705         
43706         if (this.name) {
43707             hiddenInput.name = this.name;
43708         }
43709
43710         if (this.disabled) {
43711             input.disabled = true;
43712         }
43713
43714         var clg = 12 - this.inputlg;
43715         var cmd = 12 - this.inputmd;
43716         var csm = 12 - this.inputsm;
43717         var cxs = 12 - this.inputxs;
43718         
43719         var container = {
43720             tag : 'div',
43721             cls : 'row roo-money-field',
43722             cn : [
43723                 {
43724                     tag : 'div',
43725                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43726                     cn : [
43727                         {
43728                             tag : 'div',
43729                             cls: 'roo-select2-container input-group',
43730                             cn: [
43731                                 {
43732                                     tag : 'input',
43733                                     cls : 'form-control roo-money-currency-input',
43734                                     autocomplete: 'new-password',
43735                                     readOnly : 1,
43736                                     name : this.currencyName
43737                                 },
43738                                 {
43739                                     tag :'span',
43740                                     cls : 'input-group-addon',
43741                                     cn : [
43742                                         {
43743                                             tag: 'span',
43744                                             cls: 'caret'
43745                                         }
43746                                     ]
43747                                 }
43748                             ]
43749                         }
43750                     ]
43751                 },
43752                 {
43753                     tag : 'div',
43754                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43755                     cn : [
43756                         {
43757                             tag: 'div',
43758                             cls: this.hasFeedback ? 'has-feedback' : '',
43759                             cn: [
43760                                 input
43761                             ]
43762                         }
43763                     ]
43764                 }
43765             ]
43766             
43767         };
43768         
43769         if (this.fieldLabel.length) {
43770             var indicator = {
43771                 tag: 'i',
43772                 tooltip: 'This field is required'
43773             };
43774
43775             var label = {
43776                 tag: 'label',
43777                 'for':  id,
43778                 cls: 'control-label',
43779                 cn: []
43780             };
43781
43782             var label_text = {
43783                 tag: 'span',
43784                 html: this.fieldLabel
43785             };
43786
43787             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43788             label.cn = [
43789                 indicator,
43790                 label_text
43791             ];
43792
43793             if(this.indicatorpos == 'right') {
43794                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43795                 label.cn = [
43796                     label_text,
43797                     indicator
43798                 ];
43799             }
43800
43801             if(align == 'left') {
43802                 container = {
43803                     tag: 'div',
43804                     cn: [
43805                         container
43806                     ]
43807                 };
43808
43809                 if(this.labelWidth > 12){
43810                     label.style = "width: " + this.labelWidth + 'px';
43811                 }
43812                 if(this.labelWidth < 13 && this.labelmd == 0){
43813                     this.labelmd = this.labelWidth;
43814                 }
43815                 if(this.labellg > 0){
43816                     label.cls += ' col-lg-' + this.labellg;
43817                     input.cls += ' col-lg-' + (12 - this.labellg);
43818                 }
43819                 if(this.labelmd > 0){
43820                     label.cls += ' col-md-' + this.labelmd;
43821                     container.cls += ' col-md-' + (12 - this.labelmd);
43822                 }
43823                 if(this.labelsm > 0){
43824                     label.cls += ' col-sm-' + this.labelsm;
43825                     container.cls += ' col-sm-' + (12 - this.labelsm);
43826                 }
43827                 if(this.labelxs > 0){
43828                     label.cls += ' col-xs-' + this.labelxs;
43829                     container.cls += ' col-xs-' + (12 - this.labelxs);
43830                 }
43831             }
43832         }
43833
43834         cfg.cn = [
43835             label,
43836             container,
43837             hiddenInput
43838         ];
43839         
43840         var settings = this;
43841
43842         ['xs','sm','md','lg'].map(function(size){
43843             if (settings[size]) {
43844                 cfg.cls += ' col-' + size + '-' + settings[size];
43845             }
43846         });
43847         
43848         return cfg;
43849     },
43850     
43851     initEvents : function()
43852     {
43853         this.indicator = this.indicatorEl();
43854         
43855         this.initCurrencyEvent();
43856         
43857         this.initNumberEvent();
43858     },
43859     
43860     initCurrencyEvent : function()
43861     {
43862         if (!this.store) {
43863             throw "can not find store for combo";
43864         }
43865         
43866         this.store = Roo.factory(this.store, Roo.data);
43867         this.store.parent = this;
43868         
43869         this.createList();
43870         
43871         this.triggerEl = this.el.select('.input-group-addon', true).first();
43872         
43873         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43874         
43875         var _this = this;
43876         
43877         (function(){
43878             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43879             _this.list.setWidth(lw);
43880         }).defer(100);
43881         
43882         this.list.on('mouseover', this.onViewOver, this);
43883         this.list.on('mousemove', this.onViewMove, this);
43884         this.list.on('scroll', this.onViewScroll, this);
43885         
43886         if(!this.tpl){
43887             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43888         }
43889         
43890         this.view = new Roo.View(this.list, this.tpl, {
43891             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43892         });
43893         
43894         this.view.on('click', this.onViewClick, this);
43895         
43896         this.store.on('beforeload', this.onBeforeLoad, this);
43897         this.store.on('load', this.onLoad, this);
43898         this.store.on('loadexception', this.onLoadException, this);
43899         
43900         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43901             "up" : function(e){
43902                 this.inKeyMode = true;
43903                 this.selectPrev();
43904             },
43905
43906             "down" : function(e){
43907                 if(!this.isExpanded()){
43908                     this.onTriggerClick();
43909                 }else{
43910                     this.inKeyMode = true;
43911                     this.selectNext();
43912                 }
43913             },
43914
43915             "enter" : function(e){
43916                 this.collapse();
43917                 
43918                 if(this.fireEvent("specialkey", this, e)){
43919                     this.onViewClick(false);
43920                 }
43921                 
43922                 return true;
43923             },
43924
43925             "esc" : function(e){
43926                 this.collapse();
43927             },
43928
43929             "tab" : function(e){
43930                 this.collapse();
43931                 
43932                 if(this.fireEvent("specialkey", this, e)){
43933                     this.onViewClick(false);
43934                 }
43935                 
43936                 return true;
43937             },
43938
43939             scope : this,
43940
43941             doRelay : function(foo, bar, hname){
43942                 if(hname == 'down' || this.scope.isExpanded()){
43943                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43944                 }
43945                 return true;
43946             },
43947
43948             forceKeyDown: true
43949         });
43950         
43951         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43952         
43953     },
43954     
43955     initNumberEvent : function(e)
43956     {
43957         this.inputEl().on("keydown" , this.fireKey,  this);
43958         this.inputEl().on("focus", this.onFocus,  this);
43959         this.inputEl().on("blur", this.onBlur,  this);
43960         
43961         this.inputEl().relayEvent('keyup', this);
43962         
43963         if(this.indicator){
43964             this.indicator.addClass('invisible');
43965         }
43966  
43967         this.originalValue = this.getValue();
43968         
43969         if(this.validationEvent == 'keyup'){
43970             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43971             this.inputEl().on('keyup', this.filterValidation, this);
43972         }
43973         else if(this.validationEvent !== false){
43974             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43975         }
43976         
43977         if(this.selectOnFocus){
43978             this.on("focus", this.preFocus, this);
43979             
43980         }
43981         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43982             this.inputEl().on("keypress", this.filterKeys, this);
43983         } else {
43984             this.inputEl().relayEvent('keypress', this);
43985         }
43986         
43987         var allowed = "0123456789";
43988         
43989         if(this.allowDecimals){
43990             allowed += this.decimalSeparator;
43991         }
43992         
43993         if(this.allowNegative){
43994             allowed += "-";
43995         }
43996         
43997         if(this.thousandsDelimiter) {
43998             allowed += ",";
43999         }
44000         
44001         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44002         
44003         var keyPress = function(e){
44004             
44005             var k = e.getKey();
44006             
44007             var c = e.getCharCode();
44008             
44009             if(
44010                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44011                     allowed.indexOf(String.fromCharCode(c)) === -1
44012             ){
44013                 e.stopEvent();
44014                 return;
44015             }
44016             
44017             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44018                 return;
44019             }
44020             
44021             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44022                 e.stopEvent();
44023             }
44024         };
44025         
44026         this.inputEl().on("keypress", keyPress, this);
44027         
44028     },
44029     
44030     onTriggerClick : function(e)
44031     {   
44032         if(this.disabled){
44033             return;
44034         }
44035         
44036         this.page = 0;
44037         this.loadNext = false;
44038         
44039         if(this.isExpanded()){
44040             this.collapse();
44041             return;
44042         }
44043         
44044         this.hasFocus = true;
44045         
44046         if(this.triggerAction == 'all') {
44047             this.doQuery(this.allQuery, true);
44048             return;
44049         }
44050         
44051         this.doQuery(this.getRawValue());
44052     },
44053     
44054     getCurrency : function()
44055     {   
44056         var v = this.currencyEl().getValue();
44057         
44058         return v;
44059     },
44060     
44061     restrictHeight : function()
44062     {
44063         this.list.alignTo(this.currencyEl(), this.listAlign);
44064         this.list.alignTo(this.currencyEl(), this.listAlign);
44065     },
44066     
44067     onViewClick : function(view, doFocus, el, e)
44068     {
44069         var index = this.view.getSelectedIndexes()[0];
44070         
44071         var r = this.store.getAt(index);
44072         
44073         if(r){
44074             this.onSelect(r, index);
44075         }
44076     },
44077     
44078     onSelect : function(record, index){
44079         
44080         if(this.fireEvent('beforeselect', this, record, index) !== false){
44081         
44082             this.setFromCurrencyData(index > -1 ? record.data : false);
44083             
44084             this.collapse();
44085             
44086             this.fireEvent('select', this, record, index);
44087         }
44088     },
44089     
44090     setFromCurrencyData : function(o)
44091     {
44092         var currency = '';
44093         
44094         this.lastCurrency = o;
44095         
44096         if (this.currencyField) {
44097             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44098         } else {
44099             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44100         }
44101         
44102         this.lastSelectionText = currency;
44103         
44104         //setting default currency
44105         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44106             this.setCurrency(this.defaultCurrency);
44107             return;
44108         }
44109         
44110         this.setCurrency(currency);
44111     },
44112     
44113     setFromData : function(o)
44114     {
44115         var c = {};
44116         
44117         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44118         
44119         this.setFromCurrencyData(c);
44120         
44121         var value = '';
44122         
44123         if (this.name) {
44124             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44125         } else {
44126             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44127         }
44128         
44129         this.setValue(value);
44130         
44131     },
44132     
44133     setCurrency : function(v)
44134     {   
44135         this.currencyValue = v;
44136         
44137         if(this.rendered){
44138             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44139             this.validate();
44140         }
44141     },
44142     
44143     setValue : function(v)
44144     {
44145         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44146         
44147         this.value = v;
44148         
44149         if(this.rendered){
44150             
44151             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44152             
44153             this.inputEl().dom.value = (v == '') ? '' :
44154                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44155             
44156             if(!this.allowZero && v === '0') {
44157                 this.hiddenEl().dom.value = '';
44158                 this.inputEl().dom.value = '';
44159             }
44160             
44161             this.validate();
44162         }
44163     },
44164     
44165     getRawValue : function()
44166     {
44167         var v = this.inputEl().getValue();
44168         
44169         return v;
44170     },
44171     
44172     getValue : function()
44173     {
44174         return this.fixPrecision(this.parseValue(this.getRawValue()));
44175     },
44176     
44177     parseValue : function(value)
44178     {
44179         if(this.thousandsDelimiter) {
44180             value += "";
44181             r = new RegExp(",", "g");
44182             value = value.replace(r, "");
44183         }
44184         
44185         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44186         return isNaN(value) ? '' : value;
44187         
44188     },
44189     
44190     fixPrecision : function(value)
44191     {
44192         if(this.thousandsDelimiter) {
44193             value += "";
44194             r = new RegExp(",", "g");
44195             value = value.replace(r, "");
44196         }
44197         
44198         var nan = isNaN(value);
44199         
44200         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44201             return nan ? '' : value;
44202         }
44203         return parseFloat(value).toFixed(this.decimalPrecision);
44204     },
44205     
44206     decimalPrecisionFcn : function(v)
44207     {
44208         return Math.floor(v);
44209     },
44210     
44211     validateValue : function(value)
44212     {
44213         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44214             return false;
44215         }
44216         
44217         var num = this.parseValue(value);
44218         
44219         if(isNaN(num)){
44220             this.markInvalid(String.format(this.nanText, value));
44221             return false;
44222         }
44223         
44224         if(num < this.minValue){
44225             this.markInvalid(String.format(this.minText, this.minValue));
44226             return false;
44227         }
44228         
44229         if(num > this.maxValue){
44230             this.markInvalid(String.format(this.maxText, this.maxValue));
44231             return false;
44232         }
44233         
44234         return true;
44235     },
44236     
44237     validate : function()
44238     {
44239         if(this.disabled || this.allowBlank){
44240             this.markValid();
44241             return true;
44242         }
44243         
44244         var currency = this.getCurrency();
44245         
44246         if(this.validateValue(this.getRawValue()) && currency.length){
44247             this.markValid();
44248             return true;
44249         }
44250         
44251         this.markInvalid();
44252         return false;
44253     },
44254     
44255     getName: function()
44256     {
44257         return this.name;
44258     },
44259     
44260     beforeBlur : function()
44261     {
44262         if(!this.castInt){
44263             return;
44264         }
44265         
44266         var v = this.parseValue(this.getRawValue());
44267         
44268         if(v || v == 0){
44269             this.setValue(v);
44270         }
44271     },
44272     
44273     onBlur : function()
44274     {
44275         this.beforeBlur();
44276         
44277         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44278             //this.el.removeClass(this.focusClass);
44279         }
44280         
44281         this.hasFocus = false;
44282         
44283         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44284             this.validate();
44285         }
44286         
44287         var v = this.getValue();
44288         
44289         if(String(v) !== String(this.startValue)){
44290             this.fireEvent('change', this, v, this.startValue);
44291         }
44292         
44293         this.fireEvent("blur", this);
44294     },
44295     
44296     inputEl : function()
44297     {
44298         return this.el.select('.roo-money-amount-input', true).first();
44299     },
44300     
44301     currencyEl : function()
44302     {
44303         return this.el.select('.roo-money-currency-input', true).first();
44304     },
44305     
44306     hiddenEl : function()
44307     {
44308         return this.el.select('input.hidden-number-input',true).first();
44309     }
44310     
44311 });/**
44312  * @class Roo.bootstrap.BezierSignature
44313  * @extends Roo.bootstrap.Component
44314  * Bootstrap BezierSignature class
44315  * This script refer to:
44316  *    Title: Signature Pad
44317  *    Author: szimek
44318  *    Availability: https://github.com/szimek/signature_pad
44319  *
44320  * @constructor
44321  * Create a new BezierSignature
44322  * @param {Object} config The config object
44323  */
44324
44325 Roo.bootstrap.BezierSignature = function(config){
44326     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44327     this.addEvents({
44328         "resize" : true
44329     });
44330 };
44331
44332 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44333 {
44334      
44335     curve_data: [],
44336     
44337     is_empty: true,
44338     
44339     mouse_btn_down: true,
44340     
44341     /**
44342      * @cfg {int} canvas height
44343      */
44344     canvas_height: '200px',
44345     
44346     /**
44347      * @cfg {float|function} Radius of a single dot.
44348      */ 
44349     dot_size: false,
44350     
44351     /**
44352      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44353      */
44354     min_width: 0.5,
44355     
44356     /**
44357      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44358      */
44359     max_width: 2.5,
44360     
44361     /**
44362      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44363      */
44364     throttle: 16,
44365     
44366     /**
44367      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44368      */
44369     min_distance: 5,
44370     
44371     /**
44372      * @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.
44373      */
44374     bg_color: 'rgba(0, 0, 0, 0)',
44375     
44376     /**
44377      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44378      */
44379     dot_color: 'black',
44380     
44381     /**
44382      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44383      */ 
44384     velocity_filter_weight: 0.7,
44385     
44386     /**
44387      * @cfg {function} Callback when stroke begin. 
44388      */
44389     onBegin: false,
44390     
44391     /**
44392      * @cfg {function} Callback when stroke end.
44393      */
44394     onEnd: false,
44395     
44396     getAutoCreate : function()
44397     {
44398         var cls = 'roo-signature column';
44399         
44400         if(this.cls){
44401             cls += ' ' + this.cls;
44402         }
44403         
44404         var col_sizes = [
44405             'lg',
44406             'md',
44407             'sm',
44408             'xs'
44409         ];
44410         
44411         for(var i = 0; i < col_sizes.length; i++) {
44412             if(this[col_sizes[i]]) {
44413                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44414             }
44415         }
44416         
44417         var cfg = {
44418             tag: 'div',
44419             cls: cls,
44420             cn: [
44421                 {
44422                     tag: 'div',
44423                     cls: 'roo-signature-body',
44424                     cn: [
44425                         {
44426                             tag: 'canvas',
44427                             cls: 'roo-signature-body-canvas',
44428                             height: this.canvas_height,
44429                             width: this.canvas_width
44430                         }
44431                     ]
44432                 },
44433                 {
44434                     tag: 'input',
44435                     type: 'file',
44436                     style: 'display: none'
44437                 }
44438             ]
44439         };
44440         
44441         return cfg;
44442     },
44443     
44444     initEvents: function() 
44445     {
44446         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44447         
44448         var canvas = this.canvasEl();
44449         
44450         // mouse && touch event swapping...
44451         canvas.dom.style.touchAction = 'none';
44452         canvas.dom.style.msTouchAction = 'none';
44453         
44454         this.mouse_btn_down = false;
44455         canvas.on('mousedown', this._handleMouseDown, this);
44456         canvas.on('mousemove', this._handleMouseMove, this);
44457         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44458         
44459         if (window.PointerEvent) {
44460             canvas.on('pointerdown', this._handleMouseDown, this);
44461             canvas.on('pointermove', this._handleMouseMove, this);
44462             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44463         }
44464         
44465         if ('ontouchstart' in window) {
44466             canvas.on('touchstart', this._handleTouchStart, this);
44467             canvas.on('touchmove', this._handleTouchMove, this);
44468             canvas.on('touchend', this._handleTouchEnd, this);
44469         }
44470         
44471         Roo.EventManager.onWindowResize(this.resize, this, true);
44472         
44473         // file input event
44474         this.fileEl().on('change', this.uploadImage, this);
44475         
44476         this.clear();
44477         
44478         this.resize();
44479     },
44480     
44481     resize: function(){
44482         
44483         var canvas = this.canvasEl().dom;
44484         var ctx = this.canvasElCtx();
44485         var img_data = false;
44486         
44487         if(canvas.width > 0) {
44488             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44489         }
44490         // setting canvas width will clean img data
44491         canvas.width = 0;
44492         
44493         var style = window.getComputedStyle ? 
44494             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44495             
44496         var padding_left = parseInt(style.paddingLeft) || 0;
44497         var padding_right = parseInt(style.paddingRight) || 0;
44498         
44499         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44500         
44501         if(img_data) {
44502             ctx.putImageData(img_data, 0, 0);
44503         }
44504     },
44505     
44506     _handleMouseDown: function(e)
44507     {
44508         if (e.browserEvent.which === 1) {
44509             this.mouse_btn_down = true;
44510             this.strokeBegin(e);
44511         }
44512     },
44513     
44514     _handleMouseMove: function (e)
44515     {
44516         if (this.mouse_btn_down) {
44517             this.strokeMoveUpdate(e);
44518         }
44519     },
44520     
44521     _handleMouseUp: function (e)
44522     {
44523         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44524             this.mouse_btn_down = false;
44525             this.strokeEnd(e);
44526         }
44527     },
44528     
44529     _handleTouchStart: function (e) {
44530         
44531         e.preventDefault();
44532         if (e.browserEvent.targetTouches.length === 1) {
44533             // var touch = e.browserEvent.changedTouches[0];
44534             // this.strokeBegin(touch);
44535             
44536              this.strokeBegin(e); // assume e catching the correct xy...
44537         }
44538     },
44539     
44540     _handleTouchMove: function (e) {
44541         e.preventDefault();
44542         // var touch = event.targetTouches[0];
44543         // _this._strokeMoveUpdate(touch);
44544         this.strokeMoveUpdate(e);
44545     },
44546     
44547     _handleTouchEnd: function (e) {
44548         var wasCanvasTouched = e.target === this.canvasEl().dom;
44549         if (wasCanvasTouched) {
44550             e.preventDefault();
44551             // var touch = event.changedTouches[0];
44552             // _this._strokeEnd(touch);
44553             this.strokeEnd(e);
44554         }
44555     },
44556     
44557     reset: function () {
44558         this._lastPoints = [];
44559         this._lastVelocity = 0;
44560         this._lastWidth = (this.min_width + this.max_width) / 2;
44561         this.canvasElCtx().fillStyle = this.dot_color;
44562     },
44563     
44564     strokeMoveUpdate: function(e)
44565     {
44566         this.strokeUpdate(e);
44567         
44568         if (this.throttle) {
44569             this.throttleStroke(this.strokeUpdate, this.throttle);
44570         }
44571         else {
44572             this.strokeUpdate(e);
44573         }
44574     },
44575     
44576     strokeBegin: function(e)
44577     {
44578         var newPointGroup = {
44579             color: this.dot_color,
44580             points: []
44581         };
44582         
44583         if (typeof this.onBegin === 'function') {
44584             this.onBegin(e);
44585         }
44586         
44587         this.curve_data.push(newPointGroup);
44588         this.reset();
44589         this.strokeUpdate(e);
44590     },
44591     
44592     strokeUpdate: function(e)
44593     {
44594         var rect = this.canvasEl().dom.getBoundingClientRect();
44595         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44596         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44597         var lastPoints = lastPointGroup.points;
44598         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44599         var isLastPointTooClose = lastPoint
44600             ? point.distanceTo(lastPoint) <= this.min_distance
44601             : false;
44602         var color = lastPointGroup.color;
44603         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44604             var curve = this.addPoint(point);
44605             if (!lastPoint) {
44606                 this.drawDot({color: color, point: point});
44607             }
44608             else if (curve) {
44609                 this.drawCurve({color: color, curve: curve});
44610             }
44611             lastPoints.push({
44612                 time: point.time,
44613                 x: point.x,
44614                 y: point.y
44615             });
44616         }
44617     },
44618     
44619     strokeEnd: function(e)
44620     {
44621         this.strokeUpdate(e);
44622         if (typeof this.onEnd === 'function') {
44623             this.onEnd(e);
44624         }
44625     },
44626     
44627     addPoint:  function (point) {
44628         var _lastPoints = this._lastPoints;
44629         _lastPoints.push(point);
44630         if (_lastPoints.length > 2) {
44631             if (_lastPoints.length === 3) {
44632                 _lastPoints.unshift(_lastPoints[0]);
44633             }
44634             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44635             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44636             _lastPoints.shift();
44637             return curve;
44638         }
44639         return null;
44640     },
44641     
44642     calculateCurveWidths: function (startPoint, endPoint) {
44643         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44644             (1 - this.velocity_filter_weight) * this._lastVelocity;
44645
44646         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44647         var widths = {
44648             end: newWidth,
44649             start: this._lastWidth
44650         };
44651         
44652         this._lastVelocity = velocity;
44653         this._lastWidth = newWidth;
44654         return widths;
44655     },
44656     
44657     drawDot: function (_a) {
44658         var color = _a.color, point = _a.point;
44659         var ctx = this.canvasElCtx();
44660         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44661         ctx.beginPath();
44662         this.drawCurveSegment(point.x, point.y, width);
44663         ctx.closePath();
44664         ctx.fillStyle = color;
44665         ctx.fill();
44666     },
44667     
44668     drawCurve: function (_a) {
44669         var color = _a.color, curve = _a.curve;
44670         var ctx = this.canvasElCtx();
44671         var widthDelta = curve.endWidth - curve.startWidth;
44672         var drawSteps = Math.floor(curve.length()) * 2;
44673         ctx.beginPath();
44674         ctx.fillStyle = color;
44675         for (var i = 0; i < drawSteps; i += 1) {
44676         var t = i / drawSteps;
44677         var tt = t * t;
44678         var ttt = tt * t;
44679         var u = 1 - t;
44680         var uu = u * u;
44681         var uuu = uu * u;
44682         var x = uuu * curve.startPoint.x;
44683         x += 3 * uu * t * curve.control1.x;
44684         x += 3 * u * tt * curve.control2.x;
44685         x += ttt * curve.endPoint.x;
44686         var y = uuu * curve.startPoint.y;
44687         y += 3 * uu * t * curve.control1.y;
44688         y += 3 * u * tt * curve.control2.y;
44689         y += ttt * curve.endPoint.y;
44690         var width = curve.startWidth + ttt * widthDelta;
44691         this.drawCurveSegment(x, y, width);
44692         }
44693         ctx.closePath();
44694         ctx.fill();
44695     },
44696     
44697     drawCurveSegment: function (x, y, width) {
44698         var ctx = this.canvasElCtx();
44699         ctx.moveTo(x, y);
44700         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44701         this.is_empty = false;
44702     },
44703     
44704     clear: function()
44705     {
44706         var ctx = this.canvasElCtx();
44707         var canvas = this.canvasEl().dom;
44708         ctx.fillStyle = this.bg_color;
44709         ctx.clearRect(0, 0, canvas.width, canvas.height);
44710         ctx.fillRect(0, 0, canvas.width, canvas.height);
44711         this.curve_data = [];
44712         this.reset();
44713         this.is_empty = true;
44714     },
44715     
44716     fileEl: function()
44717     {
44718         return  this.el.select('input',true).first();
44719     },
44720     
44721     canvasEl: function()
44722     {
44723         return this.el.select('canvas',true).first();
44724     },
44725     
44726     canvasElCtx: function()
44727     {
44728         return this.el.select('canvas',true).first().dom.getContext('2d');
44729     },
44730     
44731     getImage: function(type)
44732     {
44733         if(this.is_empty) {
44734             return false;
44735         }
44736         
44737         // encryption ?
44738         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44739     },
44740     
44741     drawFromImage: function(img_src)
44742     {
44743         var img = new Image();
44744         
44745         img.onload = function(){
44746             this.canvasElCtx().drawImage(img, 0, 0);
44747         }.bind(this);
44748         
44749         img.src = img_src;
44750         
44751         this.is_empty = false;
44752     },
44753     
44754     selectImage: function()
44755     {
44756         this.fileEl().dom.click();
44757     },
44758     
44759     uploadImage: function(e)
44760     {
44761         var reader = new FileReader();
44762         
44763         reader.onload = function(e){
44764             var img = new Image();
44765             img.onload = function(){
44766                 this.reset();
44767                 this.canvasElCtx().drawImage(img, 0, 0);
44768             }.bind(this);
44769             img.src = e.target.result;
44770         }.bind(this);
44771         
44772         reader.readAsDataURL(e.target.files[0]);
44773     },
44774     
44775     // Bezier Point Constructor
44776     Point: (function () {
44777         function Point(x, y, time) {
44778             this.x = x;
44779             this.y = y;
44780             this.time = time || Date.now();
44781         }
44782         Point.prototype.distanceTo = function (start) {
44783             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44784         };
44785         Point.prototype.equals = function (other) {
44786             return this.x === other.x && this.y === other.y && this.time === other.time;
44787         };
44788         Point.prototype.velocityFrom = function (start) {
44789             return this.time !== start.time
44790             ? this.distanceTo(start) / (this.time - start.time)
44791             : 0;
44792         };
44793         return Point;
44794     }()),
44795     
44796     
44797     // Bezier Constructor
44798     Bezier: (function () {
44799         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44800             this.startPoint = startPoint;
44801             this.control2 = control2;
44802             this.control1 = control1;
44803             this.endPoint = endPoint;
44804             this.startWidth = startWidth;
44805             this.endWidth = endWidth;
44806         }
44807         Bezier.fromPoints = function (points, widths, scope) {
44808             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44809             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44810             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44811         };
44812         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44813             var dx1 = s1.x - s2.x;
44814             var dy1 = s1.y - s2.y;
44815             var dx2 = s2.x - s3.x;
44816             var dy2 = s2.y - s3.y;
44817             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44818             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44819             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44820             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44821             var dxm = m1.x - m2.x;
44822             var dym = m1.y - m2.y;
44823             var k = l2 / (l1 + l2);
44824             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44825             var tx = s2.x - cm.x;
44826             var ty = s2.y - cm.y;
44827             return {
44828                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44829                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44830             };
44831         };
44832         Bezier.prototype.length = function () {
44833             var steps = 10;
44834             var length = 0;
44835             var px;
44836             var py;
44837             for (var i = 0; i <= steps; i += 1) {
44838                 var t = i / steps;
44839                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44840                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44841                 if (i > 0) {
44842                     var xdiff = cx - px;
44843                     var ydiff = cy - py;
44844                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44845                 }
44846                 px = cx;
44847                 py = cy;
44848             }
44849             return length;
44850         };
44851         Bezier.prototype.point = function (t, start, c1, c2, end) {
44852             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44853             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44854             + (3.0 * c2 * (1.0 - t) * t * t)
44855             + (end * t * t * t);
44856         };
44857         return Bezier;
44858     }()),
44859     
44860     throttleStroke: function(fn, wait) {
44861       if (wait === void 0) { wait = 250; }
44862       var previous = 0;
44863       var timeout = null;
44864       var result;
44865       var storedContext;
44866       var storedArgs;
44867       var later = function () {
44868           previous = Date.now();
44869           timeout = null;
44870           result = fn.apply(storedContext, storedArgs);
44871           if (!timeout) {
44872               storedContext = null;
44873               storedArgs = [];
44874           }
44875       };
44876       return function wrapper() {
44877           var args = [];
44878           for (var _i = 0; _i < arguments.length; _i++) {
44879               args[_i] = arguments[_i];
44880           }
44881           var now = Date.now();
44882           var remaining = wait - (now - previous);
44883           storedContext = this;
44884           storedArgs = args;
44885           if (remaining <= 0 || remaining > wait) {
44886               if (timeout) {
44887                   clearTimeout(timeout);
44888                   timeout = null;
44889               }
44890               previous = now;
44891               result = fn.apply(storedContext, storedArgs);
44892               if (!timeout) {
44893                   storedContext = null;
44894                   storedArgs = [];
44895               }
44896           }
44897           else if (!timeout) {
44898               timeout = window.setTimeout(later, remaining);
44899           }
44900           return result;
44901       };
44902   }
44903   
44904 });
44905
44906  
44907
44908