sync
[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         if(typeof c.dataIndex == "undefined"){
7794             c.dataIndex = i;
7795         }
7796         if(typeof c.renderer == "string"){
7797             c.renderer = Roo.util.Format[c.renderer];
7798         }
7799         if(typeof c.id == "undefined"){
7800             c.id = Roo.id();
7801         }
7802         if(c.editor && c.editor.xtype){
7803             c.editor  = Roo.factory(c.editor, Roo.grid);
7804         }
7805         if(c.editor && c.editor.isFormField){
7806             c.editor = new Roo.grid.GridEditor(c.editor);
7807         }
7808         this.lookup[c.id] = c;
7809     }
7810     
7811 });
7812
7813 Roo.grid.ColumnModel.defaultRenderer = function(value)
7814 {
7815     if(typeof value == "object") {
7816         return value;
7817     }
7818         if(typeof value == "string" && value.length < 1){
7819             return "&#160;";
7820         }
7821     
7822         return String.format("{0}", value);
7823 };
7824
7825 // Alias for backwards compatibility
7826 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7827 /*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837  
7838 /**
7839  * @class Roo.LoadMask
7840  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7841  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7842  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7843  * element's UpdateManager load indicator and will be destroyed after the initial load.
7844  * @constructor
7845  * Create a new LoadMask
7846  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7847  * @param {Object} config The config object
7848  */
7849 Roo.LoadMask = function(el, config){
7850     this.el = Roo.get(el);
7851     Roo.apply(this, config);
7852     if(this.store){
7853         this.store.on('beforeload', this.onBeforeLoad, this);
7854         this.store.on('load', this.onLoad, this);
7855         this.store.on('loadexception', this.onLoadException, this);
7856         this.removeMask = false;
7857     }else{
7858         var um = this.el.getUpdateManager();
7859         um.showLoadIndicator = false; // disable the default indicator
7860         um.on('beforeupdate', this.onBeforeLoad, this);
7861         um.on('update', this.onLoad, this);
7862         um.on('failure', this.onLoad, this);
7863         this.removeMask = true;
7864     }
7865 };
7866
7867 Roo.LoadMask.prototype = {
7868     /**
7869      * @cfg {Boolean} removeMask
7870      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7871      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7872      */
7873     /**
7874      * @cfg {String} msg
7875      * The text to display in a centered loading message box (defaults to 'Loading...')
7876      */
7877     msg : 'Loading...',
7878     /**
7879      * @cfg {String} msgCls
7880      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7881      */
7882     msgCls : 'x-mask-loading',
7883
7884     /**
7885      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7886      * @type Boolean
7887      */
7888     disabled: false,
7889
7890     /**
7891      * Disables the mask to prevent it from being displayed
7892      */
7893     disable : function(){
7894        this.disabled = true;
7895     },
7896
7897     /**
7898      * Enables the mask so that it can be displayed
7899      */
7900     enable : function(){
7901         this.disabled = false;
7902     },
7903     
7904     onLoadException : function()
7905     {
7906         Roo.log(arguments);
7907         
7908         if (typeof(arguments[3]) != 'undefined') {
7909             Roo.MessageBox.alert("Error loading",arguments[3]);
7910         } 
7911         /*
7912         try {
7913             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7914                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7915             }   
7916         } catch(e) {
7917             
7918         }
7919         */
7920     
7921         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7922     },
7923     // private
7924     onLoad : function()
7925     {
7926         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7927     },
7928
7929     // private
7930     onBeforeLoad : function(){
7931         if(!this.disabled){
7932             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7933         }
7934     },
7935
7936     // private
7937     destroy : function(){
7938         if(this.store){
7939             this.store.un('beforeload', this.onBeforeLoad, this);
7940             this.store.un('load', this.onLoad, this);
7941             this.store.un('loadexception', this.onLoadException, this);
7942         }else{
7943             var um = this.el.getUpdateManager();
7944             um.un('beforeupdate', this.onBeforeLoad, this);
7945             um.un('update', this.onLoad, this);
7946             um.un('failure', this.onLoad, this);
7947         }
7948     }
7949 };/*
7950  * - LGPL
7951  *
7952  * table
7953  * 
7954  */
7955
7956 /**
7957  * @class Roo.bootstrap.Table
7958  * @extends Roo.bootstrap.Component
7959  * Bootstrap Table class
7960  * @cfg {String} cls table class
7961  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7962  * @cfg {String} bgcolor Specifies the background color for a table
7963  * @cfg {Number} border Specifies whether the table cells should have borders or not
7964  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7965  * @cfg {Number} cellspacing Specifies the space between cells
7966  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7967  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7968  * @cfg {String} sortable Specifies that the table should be sortable
7969  * @cfg {String} summary Specifies a summary of the content of a table
7970  * @cfg {Number} width Specifies the width of a table
7971  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7972  * 
7973  * @cfg {boolean} striped Should the rows be alternative striped
7974  * @cfg {boolean} bordered Add borders to the table
7975  * @cfg {boolean} hover Add hover highlighting
7976  * @cfg {boolean} condensed Format condensed
7977  * @cfg {boolean} responsive Format condensed
7978  * @cfg {Boolean} loadMask (true|false) default false
7979  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7980  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7981  * @cfg {Boolean} rowSelection (true|false) default false
7982  * @cfg {Boolean} cellSelection (true|false) default false
7983  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7984  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7985  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7986  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7987  
7988  * 
7989  * @constructor
7990  * Create a new Table
7991  * @param {Object} config The config object
7992  */
7993
7994 Roo.bootstrap.Table = function(config){
7995     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7996     
7997   
7998     
7999     // BC...
8000     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8001     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8002     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8003     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8004     
8005     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8006     if (this.sm) {
8007         this.sm.grid = this;
8008         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8009         this.sm = this.selModel;
8010         this.sm.xmodule = this.xmodule || false;
8011     }
8012     
8013     if (this.cm && typeof(this.cm.config) == 'undefined') {
8014         this.colModel = new Roo.grid.ColumnModel(this.cm);
8015         this.cm = this.colModel;
8016         this.cm.xmodule = this.xmodule || false;
8017     }
8018     if (this.store) {
8019         this.store= Roo.factory(this.store, Roo.data);
8020         this.ds = this.store;
8021         this.ds.xmodule = this.xmodule || false;
8022          
8023     }
8024     if (this.footer && this.store) {
8025         this.footer.dataSource = this.ds;
8026         this.footer = Roo.factory(this.footer);
8027     }
8028     
8029     /** @private */
8030     this.addEvents({
8031         /**
8032          * @event cellclick
8033          * Fires when a cell is clicked
8034          * @param {Roo.bootstrap.Table} this
8035          * @param {Roo.Element} el
8036          * @param {Number} rowIndex
8037          * @param {Number} columnIndex
8038          * @param {Roo.EventObject} e
8039          */
8040         "cellclick" : true,
8041         /**
8042          * @event celldblclick
8043          * Fires when a cell is double clicked
8044          * @param {Roo.bootstrap.Table} this
8045          * @param {Roo.Element} el
8046          * @param {Number} rowIndex
8047          * @param {Number} columnIndex
8048          * @param {Roo.EventObject} e
8049          */
8050         "celldblclick" : true,
8051         /**
8052          * @event rowclick
8053          * Fires when a row is clicked
8054          * @param {Roo.bootstrap.Table} this
8055          * @param {Roo.Element} el
8056          * @param {Number} rowIndex
8057          * @param {Roo.EventObject} e
8058          */
8059         "rowclick" : true,
8060         /**
8061          * @event rowdblclick
8062          * Fires when a row is double clicked
8063          * @param {Roo.bootstrap.Table} this
8064          * @param {Roo.Element} el
8065          * @param {Number} rowIndex
8066          * @param {Roo.EventObject} e
8067          */
8068         "rowdblclick" : true,
8069         /**
8070          * @event mouseover
8071          * Fires when a mouseover occur
8072          * @param {Roo.bootstrap.Table} this
8073          * @param {Roo.Element} el
8074          * @param {Number} rowIndex
8075          * @param {Number} columnIndex
8076          * @param {Roo.EventObject} e
8077          */
8078         "mouseover" : true,
8079         /**
8080          * @event mouseout
8081          * Fires when a mouseout occur
8082          * @param {Roo.bootstrap.Table} this
8083          * @param {Roo.Element} el
8084          * @param {Number} rowIndex
8085          * @param {Number} columnIndex
8086          * @param {Roo.EventObject} e
8087          */
8088         "mouseout" : true,
8089         /**
8090          * @event rowclass
8091          * Fires when a row is rendered, so you can change add a style to it.
8092          * @param {Roo.bootstrap.Table} this
8093          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8094          */
8095         'rowclass' : true,
8096           /**
8097          * @event rowsrendered
8098          * Fires when all the  rows have been rendered
8099          * @param {Roo.bootstrap.Table} this
8100          */
8101         'rowsrendered' : true,
8102         /**
8103          * @event contextmenu
8104          * The raw contextmenu event for the entire grid.
8105          * @param {Roo.EventObject} e
8106          */
8107         "contextmenu" : true,
8108         /**
8109          * @event rowcontextmenu
8110          * Fires when a row is right clicked
8111          * @param {Roo.bootstrap.Table} this
8112          * @param {Number} rowIndex
8113          * @param {Roo.EventObject} e
8114          */
8115         "rowcontextmenu" : true,
8116         /**
8117          * @event cellcontextmenu
8118          * Fires when a cell is right clicked
8119          * @param {Roo.bootstrap.Table} this
8120          * @param {Number} rowIndex
8121          * @param {Number} cellIndex
8122          * @param {Roo.EventObject} e
8123          */
8124          "cellcontextmenu" : true,
8125          /**
8126          * @event headercontextmenu
8127          * Fires when a header is right clicked
8128          * @param {Roo.bootstrap.Table} this
8129          * @param {Number} columnIndex
8130          * @param {Roo.EventObject} e
8131          */
8132         "headercontextmenu" : true
8133     });
8134 };
8135
8136 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8137     
8138     cls: false,
8139     align: false,
8140     bgcolor: false,
8141     border: false,
8142     cellpadding: false,
8143     cellspacing: false,
8144     frame: false,
8145     rules: false,
8146     sortable: false,
8147     summary: false,
8148     width: false,
8149     striped : false,
8150     scrollBody : false,
8151     bordered: false,
8152     hover:  false,
8153     condensed : false,
8154     responsive : false,
8155     sm : false,
8156     cm : false,
8157     store : false,
8158     loadMask : false,
8159     footerShow : true,
8160     headerShow : true,
8161   
8162     rowSelection : false,
8163     cellSelection : false,
8164     layout : false,
8165     
8166     // Roo.Element - the tbody
8167     mainBody: false,
8168     // Roo.Element - thead element
8169     mainHead: false,
8170     
8171     container: false, // used by gridpanel...
8172     
8173     lazyLoad : false,
8174     
8175     CSS : Roo.util.CSS,
8176     
8177     auto_hide_footer : false,
8178     
8179     getAutoCreate : function()
8180     {
8181         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8182         
8183         cfg = {
8184             tag: 'table',
8185             cls : 'table',
8186             cn : []
8187         };
8188         if (this.scrollBody) {
8189             cfg.cls += ' table-body-fixed';
8190         }    
8191         if (this.striped) {
8192             cfg.cls += ' table-striped';
8193         }
8194         
8195         if (this.hover) {
8196             cfg.cls += ' table-hover';
8197         }
8198         if (this.bordered) {
8199             cfg.cls += ' table-bordered';
8200         }
8201         if (this.condensed) {
8202             cfg.cls += ' table-condensed';
8203         }
8204         if (this.responsive) {
8205             cfg.cls += ' table-responsive';
8206         }
8207         
8208         if (this.cls) {
8209             cfg.cls+=  ' ' +this.cls;
8210         }
8211         
8212         // this lot should be simplifed...
8213         var _t = this;
8214         var cp = [
8215             'align',
8216             'bgcolor',
8217             'border',
8218             'cellpadding',
8219             'cellspacing',
8220             'frame',
8221             'rules',
8222             'sortable',
8223             'summary',
8224             'width'
8225         ].forEach(function(k) {
8226             if (_t[k]) {
8227                 cfg[k] = _t[k];
8228             }
8229         });
8230         
8231         
8232         if (this.layout) {
8233             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8234         }
8235         
8236         if(this.store || this.cm){
8237             if(this.headerShow){
8238                 cfg.cn.push(this.renderHeader());
8239             }
8240             
8241             cfg.cn.push(this.renderBody());
8242             
8243             if(this.footerShow){
8244                 cfg.cn.push(this.renderFooter());
8245             }
8246             // where does this come from?
8247             //cfg.cls+=  ' TableGrid';
8248         }
8249         
8250         return { cn : [ cfg ] };
8251     },
8252     
8253     initEvents : function()
8254     {   
8255         if(!this.store || !this.cm){
8256             return;
8257         }
8258         if (this.selModel) {
8259             this.selModel.initEvents();
8260         }
8261         
8262         
8263         //Roo.log('initEvents with ds!!!!');
8264         
8265         this.mainBody = this.el.select('tbody', true).first();
8266         this.mainHead = this.el.select('thead', true).first();
8267         this.mainFoot = this.el.select('tfoot', true).first();
8268         
8269         
8270         
8271         var _this = this;
8272         
8273         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8274             e.on('click', _this.sort, _this);
8275         });
8276         
8277         this.mainBody.on("click", this.onClick, this);
8278         this.mainBody.on("dblclick", this.onDblClick, this);
8279         
8280         // why is this done????? = it breaks dialogs??
8281         //this.parent().el.setStyle('position', 'relative');
8282         
8283         
8284         if (this.footer) {
8285             this.footer.parentId = this.id;
8286             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8287             
8288             if(this.lazyLoad){
8289                 this.el.select('tfoot tr td').first().addClass('hide');
8290             }
8291         } 
8292         
8293         if(this.loadMask) {
8294             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8295         }
8296         
8297         this.store.on('load', this.onLoad, this);
8298         this.store.on('beforeload', this.onBeforeLoad, this);
8299         this.store.on('update', this.onUpdate, this);
8300         this.store.on('add', this.onAdd, this);
8301         this.store.on("clear", this.clear, this);
8302         
8303         this.el.on("contextmenu", this.onContextMenu, this);
8304         
8305         this.mainBody.on('scroll', this.onBodyScroll, this);
8306         
8307         this.cm.on("headerchange", this.onHeaderChange, this);
8308         
8309         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8310         
8311     },
8312     
8313     onContextMenu : function(e, t)
8314     {
8315         this.processEvent("contextmenu", e);
8316     },
8317     
8318     processEvent : function(name, e)
8319     {
8320         if (name != 'touchstart' ) {
8321             this.fireEvent(name, e);    
8322         }
8323         
8324         var t = e.getTarget();
8325         
8326         var cell = Roo.get(t);
8327         
8328         if(!cell){
8329             return;
8330         }
8331         
8332         if(cell.findParent('tfoot', false, true)){
8333             return;
8334         }
8335         
8336         if(cell.findParent('thead', false, true)){
8337             
8338             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8339                 cell = Roo.get(t).findParent('th', false, true);
8340                 if (!cell) {
8341                     Roo.log("failed to find th in thead?");
8342                     Roo.log(e.getTarget());
8343                     return;
8344                 }
8345             }
8346             
8347             var cellIndex = cell.dom.cellIndex;
8348             
8349             var ename = name == 'touchstart' ? 'click' : name;
8350             this.fireEvent("header" + ename, this, cellIndex, e);
8351             
8352             return;
8353         }
8354         
8355         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8356             cell = Roo.get(t).findParent('td', false, true);
8357             if (!cell) {
8358                 Roo.log("failed to find th in tbody?");
8359                 Roo.log(e.getTarget());
8360                 return;
8361             }
8362         }
8363         
8364         var row = cell.findParent('tr', false, true);
8365         var cellIndex = cell.dom.cellIndex;
8366         var rowIndex = row.dom.rowIndex - 1;
8367         
8368         if(row !== false){
8369             
8370             this.fireEvent("row" + name, this, rowIndex, e);
8371             
8372             if(cell !== false){
8373             
8374                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8375             }
8376         }
8377         
8378     },
8379     
8380     onMouseover : function(e, el)
8381     {
8382         var cell = Roo.get(el);
8383         
8384         if(!cell){
8385             return;
8386         }
8387         
8388         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8389             cell = cell.findParent('td', false, true);
8390         }
8391         
8392         var row = cell.findParent('tr', false, true);
8393         var cellIndex = cell.dom.cellIndex;
8394         var rowIndex = row.dom.rowIndex - 1; // start from 0
8395         
8396         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8397         
8398     },
8399     
8400     onMouseout : function(e, el)
8401     {
8402         var cell = Roo.get(el);
8403         
8404         if(!cell){
8405             return;
8406         }
8407         
8408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8409             cell = cell.findParent('td', false, true);
8410         }
8411         
8412         var row = cell.findParent('tr', false, true);
8413         var cellIndex = cell.dom.cellIndex;
8414         var rowIndex = row.dom.rowIndex - 1; // start from 0
8415         
8416         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8417         
8418     },
8419     
8420     onClick : function(e, el)
8421     {
8422         var cell = Roo.get(el);
8423         
8424         if(!cell || (!this.cellSelection && !this.rowSelection)){
8425             return;
8426         }
8427         
8428         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8429             cell = cell.findParent('td', false, true);
8430         }
8431         
8432         if(!cell || typeof(cell) == 'undefined'){
8433             return;
8434         }
8435         
8436         var row = cell.findParent('tr', false, true);
8437         
8438         if(!row || typeof(row) == 'undefined'){
8439             return;
8440         }
8441         
8442         var cellIndex = cell.dom.cellIndex;
8443         var rowIndex = this.getRowIndex(row);
8444         
8445         // why??? - should these not be based on SelectionModel?
8446         if(this.cellSelection){
8447             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8448         }
8449         
8450         if(this.rowSelection){
8451             this.fireEvent('rowclick', this, row, rowIndex, e);
8452         }
8453         
8454         
8455     },
8456         
8457     onDblClick : function(e,el)
8458     {
8459         var cell = Roo.get(el);
8460         
8461         if(!cell || (!this.cellSelection && !this.rowSelection)){
8462             return;
8463         }
8464         
8465         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8466             cell = cell.findParent('td', false, true);
8467         }
8468         
8469         if(!cell || typeof(cell) == 'undefined'){
8470             return;
8471         }
8472         
8473         var row = cell.findParent('tr', false, true);
8474         
8475         if(!row || typeof(row) == 'undefined'){
8476             return;
8477         }
8478         
8479         var cellIndex = cell.dom.cellIndex;
8480         var rowIndex = this.getRowIndex(row);
8481         
8482         if(this.cellSelection){
8483             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8484         }
8485         
8486         if(this.rowSelection){
8487             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8488         }
8489     },
8490     
8491     sort : function(e,el)
8492     {
8493         var col = Roo.get(el);
8494         
8495         if(!col.hasClass('sortable')){
8496             return;
8497         }
8498         
8499         var sort = col.attr('sort');
8500         var dir = 'ASC';
8501         
8502         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8503             dir = 'DESC';
8504         }
8505         
8506         this.store.sortInfo = {field : sort, direction : dir};
8507         
8508         if (this.footer) {
8509             Roo.log("calling footer first");
8510             this.footer.onClick('first');
8511         } else {
8512         
8513             this.store.load({ params : { start : 0 } });
8514         }
8515     },
8516     
8517     renderHeader : function()
8518     {
8519         var header = {
8520             tag: 'thead',
8521             cn : []
8522         };
8523         
8524         var cm = this.cm;
8525         this.totalWidth = 0;
8526         
8527         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8528             
8529             var config = cm.config[i];
8530             
8531             var c = {
8532                 tag: 'th',
8533                 cls : 'x-hcol-' + i,
8534                 style : '',
8535                 html: cm.getColumnHeader(i)
8536             };
8537             
8538             var hh = '';
8539             
8540             if(typeof(config.sortable) != 'undefined' && config.sortable){
8541                 c.cls = 'sortable';
8542                 c.html = '<i class="glyphicon"></i>' + c.html;
8543             }
8544             
8545             // could use BS4 hidden-..-down 
8546             
8547             if(typeof(config.lgHeader) != 'undefined'){
8548                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8549             }
8550             
8551             if(typeof(config.mdHeader) != 'undefined'){
8552                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8553             }
8554             
8555             if(typeof(config.smHeader) != 'undefined'){
8556                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8557             }
8558             
8559             if(typeof(config.xsHeader) != 'undefined'){
8560                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8561             }
8562             
8563             if(hh.length){
8564                 c.html = hh;
8565             }
8566             
8567             if(typeof(config.tooltip) != 'undefined'){
8568                 c.tooltip = config.tooltip;
8569             }
8570             
8571             if(typeof(config.colspan) != 'undefined'){
8572                 c.colspan = config.colspan;
8573             }
8574             
8575             if(typeof(config.hidden) != 'undefined' && config.hidden){
8576                 c.style += ' display:none;';
8577             }
8578             
8579             if(typeof(config.dataIndex) != 'undefined'){
8580                 c.sort = config.dataIndex;
8581             }
8582             
8583            
8584             
8585             if(typeof(config.align) != 'undefined' && config.align.length){
8586                 c.style += ' text-align:' + config.align + ';';
8587             }
8588             
8589             if(typeof(config.width) != 'undefined'){
8590                 c.style += ' width:' + config.width + 'px;';
8591                 this.totalWidth += config.width;
8592             } else {
8593                 this.totalWidth += 100; // assume minimum of 100 per column?
8594             }
8595             
8596             if(typeof(config.cls) != 'undefined'){
8597                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8598             }
8599             
8600             ['xs','sm','md','lg'].map(function(size){
8601                 
8602                 if(typeof(config[size]) == 'undefined'){
8603                     return;
8604                 }
8605                  
8606                 if (!config[size]) { // 0 = hidden
8607                     // BS 4 '0' is treated as hide that column and below.
8608                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8609                     return;
8610                 }
8611                 
8612                 c.cls += ' col-' + size + '-' + config[size] + (
8613                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8614                 );
8615                 
8616                 
8617             });
8618             
8619             header.cn.push(c)
8620         }
8621         
8622         return header;
8623     },
8624     
8625     renderBody : function()
8626     {
8627         var body = {
8628             tag: 'tbody',
8629             cn : [
8630                 {
8631                     tag: 'tr',
8632                     cn : [
8633                         {
8634                             tag : 'td',
8635                             colspan :  this.cm.getColumnCount()
8636                         }
8637                     ]
8638                 }
8639             ]
8640         };
8641         
8642         return body;
8643     },
8644     
8645     renderFooter : function()
8646     {
8647         var footer = {
8648             tag: 'tfoot',
8649             cn : [
8650                 {
8651                     tag: 'tr',
8652                     cn : [
8653                         {
8654                             tag : 'td',
8655                             colspan :  this.cm.getColumnCount()
8656                         }
8657                     ]
8658                 }
8659             ]
8660         };
8661         
8662         return footer;
8663     },
8664     
8665     
8666     
8667     onLoad : function()
8668     {
8669 //        Roo.log('ds onload');
8670         this.clear();
8671         
8672         var _this = this;
8673         var cm = this.cm;
8674         var ds = this.store;
8675         
8676         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8677             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8678             if (_this.store.sortInfo) {
8679                     
8680                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8681                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8682                 }
8683                 
8684                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8685                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8686                 }
8687             }
8688         });
8689         
8690         var tbody =  this.mainBody;
8691               
8692         if(ds.getCount() > 0){
8693             ds.data.each(function(d,rowIndex){
8694                 var row =  this.renderRow(cm, ds, rowIndex);
8695                 
8696                 tbody.createChild(row);
8697                 
8698                 var _this = this;
8699                 
8700                 if(row.cellObjects.length){
8701                     Roo.each(row.cellObjects, function(r){
8702                         _this.renderCellObject(r);
8703                     })
8704                 }
8705                 
8706             }, this);
8707         }
8708         
8709         var tfoot = this.el.select('tfoot', true).first();
8710         
8711         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8712             
8713             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8714             
8715             var total = this.ds.getTotalCount();
8716             
8717             if(this.footer.pageSize < total){
8718                 this.mainFoot.show();
8719             }
8720         }
8721         
8722         Roo.each(this.el.select('tbody td', true).elements, function(e){
8723             e.on('mouseover', _this.onMouseover, _this);
8724         });
8725         
8726         Roo.each(this.el.select('tbody td', true).elements, function(e){
8727             e.on('mouseout', _this.onMouseout, _this);
8728         });
8729         this.fireEvent('rowsrendered', this);
8730         
8731         this.autoSize();
8732     },
8733     
8734     
8735     onUpdate : function(ds,record)
8736     {
8737         this.refreshRow(record);
8738         this.autoSize();
8739     },
8740     
8741     onRemove : function(ds, record, index, isUpdate){
8742         if(isUpdate !== true){
8743             this.fireEvent("beforerowremoved", this, index, record);
8744         }
8745         var bt = this.mainBody.dom;
8746         
8747         var rows = this.el.select('tbody > tr', true).elements;
8748         
8749         if(typeof(rows[index]) != 'undefined'){
8750             bt.removeChild(rows[index].dom);
8751         }
8752         
8753 //        if(bt.rows[index]){
8754 //            bt.removeChild(bt.rows[index]);
8755 //        }
8756         
8757         if(isUpdate !== true){
8758             //this.stripeRows(index);
8759             //this.syncRowHeights(index, index);
8760             //this.layout();
8761             this.fireEvent("rowremoved", this, index, record);
8762         }
8763     },
8764     
8765     onAdd : function(ds, records, rowIndex)
8766     {
8767         //Roo.log('on Add called');
8768         // - note this does not handle multiple adding very well..
8769         var bt = this.mainBody.dom;
8770         for (var i =0 ; i < records.length;i++) {
8771             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8772             //Roo.log(records[i]);
8773             //Roo.log(this.store.getAt(rowIndex+i));
8774             this.insertRow(this.store, rowIndex + i, false);
8775             return;
8776         }
8777         
8778     },
8779     
8780     
8781     refreshRow : function(record){
8782         var ds = this.store, index;
8783         if(typeof record == 'number'){
8784             index = record;
8785             record = ds.getAt(index);
8786         }else{
8787             index = ds.indexOf(record);
8788             if (index < 0) {
8789                 return; // should not happen - but seems to 
8790             }
8791         }
8792         this.insertRow(ds, index, true);
8793         this.autoSize();
8794         this.onRemove(ds, record, index+1, true);
8795         this.autoSize();
8796         //this.syncRowHeights(index, index);
8797         //this.layout();
8798         this.fireEvent("rowupdated", this, index, record);
8799     },
8800     
8801     insertRow : function(dm, rowIndex, isUpdate){
8802         
8803         if(!isUpdate){
8804             this.fireEvent("beforerowsinserted", this, rowIndex);
8805         }
8806             //var s = this.getScrollState();
8807         var row = this.renderRow(this.cm, this.store, rowIndex);
8808         // insert before rowIndex..
8809         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8810         
8811         var _this = this;
8812                 
8813         if(row.cellObjects.length){
8814             Roo.each(row.cellObjects, function(r){
8815                 _this.renderCellObject(r);
8816             })
8817         }
8818             
8819         if(!isUpdate){
8820             this.fireEvent("rowsinserted", this, rowIndex);
8821             //this.syncRowHeights(firstRow, lastRow);
8822             //this.stripeRows(firstRow);
8823             //this.layout();
8824         }
8825         
8826     },
8827     
8828     
8829     getRowDom : function(rowIndex)
8830     {
8831         var rows = this.el.select('tbody > tr', true).elements;
8832         
8833         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8834         
8835     },
8836     // returns the object tree for a tr..
8837   
8838     
8839     renderRow : function(cm, ds, rowIndex) 
8840     {
8841         var d = ds.getAt(rowIndex);
8842         
8843         var row = {
8844             tag : 'tr',
8845             cls : 'x-row-' + rowIndex,
8846             cn : []
8847         };
8848             
8849         var cellObjects = [];
8850         
8851         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8852             var config = cm.config[i];
8853             
8854             var renderer = cm.getRenderer(i);
8855             var value = '';
8856             var id = false;
8857             
8858             if(typeof(renderer) !== 'undefined'){
8859                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8860             }
8861             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8862             // and are rendered into the cells after the row is rendered - using the id for the element.
8863             
8864             if(typeof(value) === 'object'){
8865                 id = Roo.id();
8866                 cellObjects.push({
8867                     container : id,
8868                     cfg : value 
8869                 })
8870             }
8871             
8872             var rowcfg = {
8873                 record: d,
8874                 rowIndex : rowIndex,
8875                 colIndex : i,
8876                 rowClass : ''
8877             };
8878
8879             this.fireEvent('rowclass', this, rowcfg);
8880             
8881             var td = {
8882                 tag: 'td',
8883                 cls : rowcfg.rowClass + ' x-col-' + i,
8884                 style: '',
8885                 html: (typeof(value) === 'object') ? '' : value
8886             };
8887             
8888             if (id) {
8889                 td.id = id;
8890             }
8891             
8892             if(typeof(config.colspan) != 'undefined'){
8893                 td.colspan = config.colspan;
8894             }
8895             
8896             if(typeof(config.hidden) != 'undefined' && config.hidden){
8897                 td.style += ' display:none;';
8898             }
8899             
8900             if(typeof(config.align) != 'undefined' && config.align.length){
8901                 td.style += ' text-align:' + config.align + ';';
8902             }
8903             if(typeof(config.valign) != 'undefined' && config.valign.length){
8904                 td.style += ' vertical-align:' + config.valign + ';';
8905             }
8906             
8907             if(typeof(config.width) != 'undefined'){
8908                 td.style += ' width:' +  config.width + 'px;';
8909             }
8910             
8911             if(typeof(config.cursor) != 'undefined'){
8912                 td.style += ' cursor:' +  config.cursor + ';';
8913             }
8914             
8915             if(typeof(config.cls) != 'undefined'){
8916                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8917             }
8918             
8919             ['xs','sm','md','lg'].map(function(size){
8920                 
8921                 if(typeof(config[size]) == 'undefined'){
8922                     return;
8923                 }
8924                 
8925                 
8926                   
8927                 if (!config[size]) { // 0 = hidden
8928                     // BS 4 '0' is treated as hide that column and below.
8929                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8930                     return;
8931                 }
8932                 
8933                 td.cls += ' col-' + size + '-' + config[size] + (
8934                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8935                 );
8936                  
8937
8938             });
8939             
8940             row.cn.push(td);
8941            
8942         }
8943         
8944         row.cellObjects = cellObjects;
8945         
8946         return row;
8947           
8948     },
8949     
8950     
8951     
8952     onBeforeLoad : function()
8953     {
8954         
8955     },
8956      /**
8957      * Remove all rows
8958      */
8959     clear : function()
8960     {
8961         this.el.select('tbody', true).first().dom.innerHTML = '';
8962     },
8963     /**
8964      * Show or hide a row.
8965      * @param {Number} rowIndex to show or hide
8966      * @param {Boolean} state hide
8967      */
8968     setRowVisibility : function(rowIndex, state)
8969     {
8970         var bt = this.mainBody.dom;
8971         
8972         var rows = this.el.select('tbody > tr', true).elements;
8973         
8974         if(typeof(rows[rowIndex]) == 'undefined'){
8975             return;
8976         }
8977         rows[rowIndex].dom.style.display = state ? '' : 'none';
8978     },
8979     
8980     
8981     getSelectionModel : function(){
8982         if(!this.selModel){
8983             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8984         }
8985         return this.selModel;
8986     },
8987     /*
8988      * Render the Roo.bootstrap object from renderder
8989      */
8990     renderCellObject : function(r)
8991     {
8992         var _this = this;
8993         
8994         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8995         
8996         var t = r.cfg.render(r.container);
8997         
8998         if(r.cfg.cn){
8999             Roo.each(r.cfg.cn, function(c){
9000                 var child = {
9001                     container: t.getChildContainer(),
9002                     cfg: c
9003                 };
9004                 _this.renderCellObject(child);
9005             })
9006         }
9007     },
9008     
9009     getRowIndex : function(row)
9010     {
9011         var rowIndex = -1;
9012         
9013         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9014             if(el != row){
9015                 return;
9016             }
9017             
9018             rowIndex = index;
9019         });
9020         
9021         return rowIndex;
9022     },
9023      /**
9024      * Returns the grid's underlying element = used by panel.Grid
9025      * @return {Element} The element
9026      */
9027     getGridEl : function(){
9028         return this.el;
9029     },
9030      /**
9031      * Forces a resize - used by panel.Grid
9032      * @return {Element} The element
9033      */
9034     autoSize : function()
9035     {
9036         //var ctr = Roo.get(this.container.dom.parentElement);
9037         var ctr = Roo.get(this.el.dom);
9038         
9039         var thd = this.getGridEl().select('thead',true).first();
9040         var tbd = this.getGridEl().select('tbody', true).first();
9041         var tfd = this.getGridEl().select('tfoot', true).first();
9042         
9043         var cw = ctr.getWidth();
9044         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9045         
9046         if (tbd) {
9047             
9048             tbd.setWidth(ctr.getWidth());
9049             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9050             // this needs fixing for various usage - currently only hydra job advers I think..
9051             //tdb.setHeight(
9052             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9053             //); 
9054             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9055             cw -= barsize;
9056         }
9057         cw = Math.max(cw, this.totalWidth);
9058         this.getGridEl().select('tbody tr',true).setWidth(cw);
9059         
9060         // resize 'expandable coloumn?
9061         
9062         return; // we doe not have a view in this design..
9063         
9064     },
9065     onBodyScroll: function()
9066     {
9067         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9068         if(this.mainHead){
9069             this.mainHead.setStyle({
9070                 'position' : 'relative',
9071                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9072             });
9073         }
9074         
9075         if(this.lazyLoad){
9076             
9077             var scrollHeight = this.mainBody.dom.scrollHeight;
9078             
9079             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9080             
9081             var height = this.mainBody.getHeight();
9082             
9083             if(scrollHeight - height == scrollTop) {
9084                 
9085                 var total = this.ds.getTotalCount();
9086                 
9087                 if(this.footer.cursor + this.footer.pageSize < total){
9088                     
9089                     this.footer.ds.load({
9090                         params : {
9091                             start : this.footer.cursor + this.footer.pageSize,
9092                             limit : this.footer.pageSize
9093                         },
9094                         add : true
9095                     });
9096                 }
9097             }
9098             
9099         }
9100     },
9101     
9102     onHeaderChange : function()
9103     {
9104         var header = this.renderHeader();
9105         var table = this.el.select('table', true).first();
9106         
9107         this.mainHead.remove();
9108         this.mainHead = table.createChild(header, this.mainBody, false);
9109     },
9110     
9111     onHiddenChange : function(colModel, colIndex, hidden)
9112     {
9113         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9114         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9115         
9116         this.CSS.updateRule(thSelector, "display", "");
9117         this.CSS.updateRule(tdSelector, "display", "");
9118         
9119         if(hidden){
9120             this.CSS.updateRule(thSelector, "display", "none");
9121             this.CSS.updateRule(tdSelector, "display", "none");
9122         }
9123         
9124         this.onHeaderChange();
9125         this.onLoad();
9126     },
9127     
9128     setColumnWidth: function(col_index, width)
9129     {
9130         // width = "md-2 xs-2..."
9131         if(!this.colModel.config[col_index]) {
9132             return;
9133         }
9134         
9135         var w = width.split(" ");
9136         
9137         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9138         
9139         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9140         
9141         
9142         for(var j = 0; j < w.length; j++) {
9143             
9144             if(!w[j]) {
9145                 continue;
9146             }
9147             
9148             var size_cls = w[j].split("-");
9149             
9150             if(!Number.isInteger(size_cls[1] * 1)) {
9151                 continue;
9152             }
9153             
9154             if(!this.colModel.config[col_index][size_cls[0]]) {
9155                 continue;
9156             }
9157             
9158             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9159                 continue;
9160             }
9161             
9162             h_row[0].classList.replace(
9163                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9164                 "col-"+size_cls[0]+"-"+size_cls[1]
9165             );
9166             
9167             for(var i = 0; i < rows.length; i++) {
9168                 
9169                 var size_cls = w[j].split("-");
9170                 
9171                 if(!Number.isInteger(size_cls[1] * 1)) {
9172                     continue;
9173                 }
9174                 
9175                 if(!this.colModel.config[col_index][size_cls[0]]) {
9176                     continue;
9177                 }
9178                 
9179                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9180                     continue;
9181                 }
9182                 
9183                 rows[i].classList.replace(
9184                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9185                     "col-"+size_cls[0]+"-"+size_cls[1]
9186                 );
9187             }
9188             
9189             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9190         }
9191     }
9192 });
9193
9194  
9195
9196  /*
9197  * - LGPL
9198  *
9199  * table cell
9200  * 
9201  */
9202
9203 /**
9204  * @class Roo.bootstrap.TableCell
9205  * @extends Roo.bootstrap.Component
9206  * Bootstrap TableCell class
9207  * @cfg {String} html cell contain text
9208  * @cfg {String} cls cell class
9209  * @cfg {String} tag cell tag (td|th) default td
9210  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9211  * @cfg {String} align Aligns the content in a cell
9212  * @cfg {String} axis Categorizes cells
9213  * @cfg {String} bgcolor Specifies the background color of a cell
9214  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9215  * @cfg {Number} colspan Specifies the number of columns a cell should span
9216  * @cfg {String} headers Specifies one or more header cells a cell is related to
9217  * @cfg {Number} height Sets the height of a cell
9218  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9219  * @cfg {Number} rowspan Sets the number of rows a cell should span
9220  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9221  * @cfg {String} valign Vertical aligns the content in a cell
9222  * @cfg {Number} width Specifies the width of a cell
9223  * 
9224  * @constructor
9225  * Create a new TableCell
9226  * @param {Object} config The config object
9227  */
9228
9229 Roo.bootstrap.TableCell = function(config){
9230     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9231 };
9232
9233 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9234     
9235     html: false,
9236     cls: false,
9237     tag: false,
9238     abbr: false,
9239     align: false,
9240     axis: false,
9241     bgcolor: false,
9242     charoff: false,
9243     colspan: false,
9244     headers: false,
9245     height: false,
9246     nowrap: false,
9247     rowspan: false,
9248     scope: false,
9249     valign: false,
9250     width: false,
9251     
9252     
9253     getAutoCreate : function(){
9254         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9255         
9256         cfg = {
9257             tag: 'td'
9258         };
9259         
9260         if(this.tag){
9261             cfg.tag = this.tag;
9262         }
9263         
9264         if (this.html) {
9265             cfg.html=this.html
9266         }
9267         if (this.cls) {
9268             cfg.cls=this.cls
9269         }
9270         if (this.abbr) {
9271             cfg.abbr=this.abbr
9272         }
9273         if (this.align) {
9274             cfg.align=this.align
9275         }
9276         if (this.axis) {
9277             cfg.axis=this.axis
9278         }
9279         if (this.bgcolor) {
9280             cfg.bgcolor=this.bgcolor
9281         }
9282         if (this.charoff) {
9283             cfg.charoff=this.charoff
9284         }
9285         if (this.colspan) {
9286             cfg.colspan=this.colspan
9287         }
9288         if (this.headers) {
9289             cfg.headers=this.headers
9290         }
9291         if (this.height) {
9292             cfg.height=this.height
9293         }
9294         if (this.nowrap) {
9295             cfg.nowrap=this.nowrap
9296         }
9297         if (this.rowspan) {
9298             cfg.rowspan=this.rowspan
9299         }
9300         if (this.scope) {
9301             cfg.scope=this.scope
9302         }
9303         if (this.valign) {
9304             cfg.valign=this.valign
9305         }
9306         if (this.width) {
9307             cfg.width=this.width
9308         }
9309         
9310         
9311         return cfg;
9312     }
9313    
9314 });
9315
9316  
9317
9318  /*
9319  * - LGPL
9320  *
9321  * table row
9322  * 
9323  */
9324
9325 /**
9326  * @class Roo.bootstrap.TableRow
9327  * @extends Roo.bootstrap.Component
9328  * Bootstrap TableRow class
9329  * @cfg {String} cls row class
9330  * @cfg {String} align Aligns the content in a table row
9331  * @cfg {String} bgcolor Specifies a background color for a table row
9332  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9333  * @cfg {String} valign Vertical aligns the content in a table row
9334  * 
9335  * @constructor
9336  * Create a new TableRow
9337  * @param {Object} config The config object
9338  */
9339
9340 Roo.bootstrap.TableRow = function(config){
9341     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9342 };
9343
9344 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9345     
9346     cls: false,
9347     align: false,
9348     bgcolor: false,
9349     charoff: false,
9350     valign: false,
9351     
9352     getAutoCreate : function(){
9353         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9354         
9355         cfg = {
9356             tag: 'tr'
9357         };
9358             
9359         if(this.cls){
9360             cfg.cls = this.cls;
9361         }
9362         if(this.align){
9363             cfg.align = this.align;
9364         }
9365         if(this.bgcolor){
9366             cfg.bgcolor = this.bgcolor;
9367         }
9368         if(this.charoff){
9369             cfg.charoff = this.charoff;
9370         }
9371         if(this.valign){
9372             cfg.valign = this.valign;
9373         }
9374         
9375         return cfg;
9376     }
9377    
9378 });
9379
9380  
9381
9382  /*
9383  * - LGPL
9384  *
9385  * table body
9386  * 
9387  */
9388
9389 /**
9390  * @class Roo.bootstrap.TableBody
9391  * @extends Roo.bootstrap.Component
9392  * Bootstrap TableBody class
9393  * @cfg {String} cls element class
9394  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9395  * @cfg {String} align Aligns the content inside the element
9396  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9397  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9398  * 
9399  * @constructor
9400  * Create a new TableBody
9401  * @param {Object} config The config object
9402  */
9403
9404 Roo.bootstrap.TableBody = function(config){
9405     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9409     
9410     cls: false,
9411     tag: false,
9412     align: false,
9413     charoff: false,
9414     valign: false,
9415     
9416     getAutoCreate : function(){
9417         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9418         
9419         cfg = {
9420             tag: 'tbody'
9421         };
9422             
9423         if (this.cls) {
9424             cfg.cls=this.cls
9425         }
9426         if(this.tag){
9427             cfg.tag = this.tag;
9428         }
9429         
9430         if(this.align){
9431             cfg.align = this.align;
9432         }
9433         if(this.charoff){
9434             cfg.charoff = this.charoff;
9435         }
9436         if(this.valign){
9437             cfg.valign = this.valign;
9438         }
9439         
9440         return cfg;
9441     }
9442     
9443     
9444 //    initEvents : function()
9445 //    {
9446 //        
9447 //        if(!this.store){
9448 //            return;
9449 //        }
9450 //        
9451 //        this.store = Roo.factory(this.store, Roo.data);
9452 //        this.store.on('load', this.onLoad, this);
9453 //        
9454 //        this.store.load();
9455 //        
9456 //    },
9457 //    
9458 //    onLoad: function () 
9459 //    {   
9460 //        this.fireEvent('load', this);
9461 //    }
9462 //    
9463 //   
9464 });
9465
9466  
9467
9468  /*
9469  * Based on:
9470  * Ext JS Library 1.1.1
9471  * Copyright(c) 2006-2007, Ext JS, LLC.
9472  *
9473  * Originally Released Under LGPL - original licence link has changed is not relivant.
9474  *
9475  * Fork - LGPL
9476  * <script type="text/javascript">
9477  */
9478
9479 // as we use this in bootstrap.
9480 Roo.namespace('Roo.form');
9481  /**
9482  * @class Roo.form.Action
9483  * Internal Class used to handle form actions
9484  * @constructor
9485  * @param {Roo.form.BasicForm} el The form element or its id
9486  * @param {Object} config Configuration options
9487  */
9488
9489  
9490  
9491 // define the action interface
9492 Roo.form.Action = function(form, options){
9493     this.form = form;
9494     this.options = options || {};
9495 };
9496 /**
9497  * Client Validation Failed
9498  * @const 
9499  */
9500 Roo.form.Action.CLIENT_INVALID = 'client';
9501 /**
9502  * Server Validation Failed
9503  * @const 
9504  */
9505 Roo.form.Action.SERVER_INVALID = 'server';
9506  /**
9507  * Connect to Server Failed
9508  * @const 
9509  */
9510 Roo.form.Action.CONNECT_FAILURE = 'connect';
9511 /**
9512  * Reading Data from Server Failed
9513  * @const 
9514  */
9515 Roo.form.Action.LOAD_FAILURE = 'load';
9516
9517 Roo.form.Action.prototype = {
9518     type : 'default',
9519     failureType : undefined,
9520     response : undefined,
9521     result : undefined,
9522
9523     // interface method
9524     run : function(options){
9525
9526     },
9527
9528     // interface method
9529     success : function(response){
9530
9531     },
9532
9533     // interface method
9534     handleResponse : function(response){
9535
9536     },
9537
9538     // default connection failure
9539     failure : function(response){
9540         
9541         this.response = response;
9542         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9543         this.form.afterAction(this, false);
9544     },
9545
9546     processResponse : function(response){
9547         this.response = response;
9548         if(!response.responseText){
9549             return true;
9550         }
9551         this.result = this.handleResponse(response);
9552         return this.result;
9553     },
9554
9555     // utility functions used internally
9556     getUrl : function(appendParams){
9557         var url = this.options.url || this.form.url || this.form.el.dom.action;
9558         if(appendParams){
9559             var p = this.getParams();
9560             if(p){
9561                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9562             }
9563         }
9564         return url;
9565     },
9566
9567     getMethod : function(){
9568         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9569     },
9570
9571     getParams : function(){
9572         var bp = this.form.baseParams;
9573         var p = this.options.params;
9574         if(p){
9575             if(typeof p == "object"){
9576                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9577             }else if(typeof p == 'string' && bp){
9578                 p += '&' + Roo.urlEncode(bp);
9579             }
9580         }else if(bp){
9581             p = Roo.urlEncode(bp);
9582         }
9583         return p;
9584     },
9585
9586     createCallback : function(){
9587         return {
9588             success: this.success,
9589             failure: this.failure,
9590             scope: this,
9591             timeout: (this.form.timeout*1000),
9592             upload: this.form.fileUpload ? this.success : undefined
9593         };
9594     }
9595 };
9596
9597 Roo.form.Action.Submit = function(form, options){
9598     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9599 };
9600
9601 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9602     type : 'submit',
9603
9604     haveProgress : false,
9605     uploadComplete : false,
9606     
9607     // uploadProgress indicator.
9608     uploadProgress : function()
9609     {
9610         if (!this.form.progressUrl) {
9611             return;
9612         }
9613         
9614         if (!this.haveProgress) {
9615             Roo.MessageBox.progress("Uploading", "Uploading");
9616         }
9617         if (this.uploadComplete) {
9618            Roo.MessageBox.hide();
9619            return;
9620         }
9621         
9622         this.haveProgress = true;
9623    
9624         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9625         
9626         var c = new Roo.data.Connection();
9627         c.request({
9628             url : this.form.progressUrl,
9629             params: {
9630                 id : uid
9631             },
9632             method: 'GET',
9633             success : function(req){
9634                //console.log(data);
9635                 var rdata = false;
9636                 var edata;
9637                 try  {
9638                    rdata = Roo.decode(req.responseText)
9639                 } catch (e) {
9640                     Roo.log("Invalid data from server..");
9641                     Roo.log(edata);
9642                     return;
9643                 }
9644                 if (!rdata || !rdata.success) {
9645                     Roo.log(rdata);
9646                     Roo.MessageBox.alert(Roo.encode(rdata));
9647                     return;
9648                 }
9649                 var data = rdata.data;
9650                 
9651                 if (this.uploadComplete) {
9652                    Roo.MessageBox.hide();
9653                    return;
9654                 }
9655                    
9656                 if (data){
9657                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9658                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9659                     );
9660                 }
9661                 this.uploadProgress.defer(2000,this);
9662             },
9663        
9664             failure: function(data) {
9665                 Roo.log('progress url failed ');
9666                 Roo.log(data);
9667             },
9668             scope : this
9669         });
9670            
9671     },
9672     
9673     
9674     run : function()
9675     {
9676         // run get Values on the form, so it syncs any secondary forms.
9677         this.form.getValues();
9678         
9679         var o = this.options;
9680         var method = this.getMethod();
9681         var isPost = method == 'POST';
9682         if(o.clientValidation === false || this.form.isValid()){
9683             
9684             if (this.form.progressUrl) {
9685                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9686                     (new Date() * 1) + '' + Math.random());
9687                     
9688             } 
9689             
9690             
9691             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9692                 form:this.form.el.dom,
9693                 url:this.getUrl(!isPost),
9694                 method: method,
9695                 params:isPost ? this.getParams() : null,
9696                 isUpload: this.form.fileUpload,
9697                 formData : this.form.formData
9698             }));
9699             
9700             this.uploadProgress();
9701
9702         }else if (o.clientValidation !== false){ // client validation failed
9703             this.failureType = Roo.form.Action.CLIENT_INVALID;
9704             this.form.afterAction(this, false);
9705         }
9706     },
9707
9708     success : function(response)
9709     {
9710         this.uploadComplete= true;
9711         if (this.haveProgress) {
9712             Roo.MessageBox.hide();
9713         }
9714         
9715         
9716         var result = this.processResponse(response);
9717         if(result === true || result.success){
9718             this.form.afterAction(this, true);
9719             return;
9720         }
9721         if(result.errors){
9722             this.form.markInvalid(result.errors);
9723             this.failureType = Roo.form.Action.SERVER_INVALID;
9724         }
9725         this.form.afterAction(this, false);
9726     },
9727     failure : function(response)
9728     {
9729         this.uploadComplete= true;
9730         if (this.haveProgress) {
9731             Roo.MessageBox.hide();
9732         }
9733         
9734         this.response = response;
9735         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9736         this.form.afterAction(this, false);
9737     },
9738     
9739     handleResponse : function(response){
9740         if(this.form.errorReader){
9741             var rs = this.form.errorReader.read(response);
9742             var errors = [];
9743             if(rs.records){
9744                 for(var i = 0, len = rs.records.length; i < len; i++) {
9745                     var r = rs.records[i];
9746                     errors[i] = r.data;
9747                 }
9748             }
9749             if(errors.length < 1){
9750                 errors = null;
9751             }
9752             return {
9753                 success : rs.success,
9754                 errors : errors
9755             };
9756         }
9757         var ret = false;
9758         try {
9759             ret = Roo.decode(response.responseText);
9760         } catch (e) {
9761             ret = {
9762                 success: false,
9763                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9764                 errors : []
9765             };
9766         }
9767         return ret;
9768         
9769     }
9770 });
9771
9772
9773 Roo.form.Action.Load = function(form, options){
9774     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9775     this.reader = this.form.reader;
9776 };
9777
9778 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9779     type : 'load',
9780
9781     run : function(){
9782         
9783         Roo.Ajax.request(Roo.apply(
9784                 this.createCallback(), {
9785                     method:this.getMethod(),
9786                     url:this.getUrl(false),
9787                     params:this.getParams()
9788         }));
9789     },
9790
9791     success : function(response){
9792         
9793         var result = this.processResponse(response);
9794         if(result === true || !result.success || !result.data){
9795             this.failureType = Roo.form.Action.LOAD_FAILURE;
9796             this.form.afterAction(this, false);
9797             return;
9798         }
9799         this.form.clearInvalid();
9800         this.form.setValues(result.data);
9801         this.form.afterAction(this, true);
9802     },
9803
9804     handleResponse : function(response){
9805         if(this.form.reader){
9806             var rs = this.form.reader.read(response);
9807             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9808             return {
9809                 success : rs.success,
9810                 data : data
9811             };
9812         }
9813         return Roo.decode(response.responseText);
9814     }
9815 });
9816
9817 Roo.form.Action.ACTION_TYPES = {
9818     'load' : Roo.form.Action.Load,
9819     'submit' : Roo.form.Action.Submit
9820 };/*
9821  * - LGPL
9822  *
9823  * form
9824  *
9825  */
9826
9827 /**
9828  * @class Roo.bootstrap.Form
9829  * @extends Roo.bootstrap.Component
9830  * Bootstrap Form class
9831  * @cfg {String} method  GET | POST (default POST)
9832  * @cfg {String} labelAlign top | left (default top)
9833  * @cfg {String} align left  | right - for navbars
9834  * @cfg {Boolean} loadMask load mask when submit (default true)
9835
9836  *
9837  * @constructor
9838  * Create a new Form
9839  * @param {Object} config The config object
9840  */
9841
9842
9843 Roo.bootstrap.Form = function(config){
9844     
9845     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9846     
9847     Roo.bootstrap.Form.popover.apply();
9848     
9849     this.addEvents({
9850         /**
9851          * @event clientvalidation
9852          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9853          * @param {Form} this
9854          * @param {Boolean} valid true if the form has passed client-side validation
9855          */
9856         clientvalidation: true,
9857         /**
9858          * @event beforeaction
9859          * Fires before any action is performed. Return false to cancel the action.
9860          * @param {Form} this
9861          * @param {Action} action The action to be performed
9862          */
9863         beforeaction: true,
9864         /**
9865          * @event actionfailed
9866          * Fires when an action fails.
9867          * @param {Form} this
9868          * @param {Action} action The action that failed
9869          */
9870         actionfailed : true,
9871         /**
9872          * @event actioncomplete
9873          * Fires when an action is completed.
9874          * @param {Form} this
9875          * @param {Action} action The action that completed
9876          */
9877         actioncomplete : true
9878     });
9879 };
9880
9881 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9882
9883      /**
9884      * @cfg {String} method
9885      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9886      */
9887     method : 'POST',
9888     /**
9889      * @cfg {String} url
9890      * The URL to use for form actions if one isn't supplied in the action options.
9891      */
9892     /**
9893      * @cfg {Boolean} fileUpload
9894      * Set to true if this form is a file upload.
9895      */
9896
9897     /**
9898      * @cfg {Object} baseParams
9899      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9900      */
9901
9902     /**
9903      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9904      */
9905     timeout: 30,
9906     /**
9907      * @cfg {Sting} align (left|right) for navbar forms
9908      */
9909     align : 'left',
9910
9911     // private
9912     activeAction : null,
9913
9914     /**
9915      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9916      * element by passing it or its id or mask the form itself by passing in true.
9917      * @type Mixed
9918      */
9919     waitMsgTarget : false,
9920
9921     loadMask : true,
9922     
9923     /**
9924      * @cfg {Boolean} errorMask (true|false) default false
9925      */
9926     errorMask : false,
9927     
9928     /**
9929      * @cfg {Number} maskOffset Default 100
9930      */
9931     maskOffset : 100,
9932     
9933     /**
9934      * @cfg {Boolean} maskBody
9935      */
9936     maskBody : false,
9937
9938     getAutoCreate : function(){
9939
9940         var cfg = {
9941             tag: 'form',
9942             method : this.method || 'POST',
9943             id : this.id || Roo.id(),
9944             cls : ''
9945         };
9946         if (this.parent().xtype.match(/^Nav/)) {
9947             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9948
9949         }
9950
9951         if (this.labelAlign == 'left' ) {
9952             cfg.cls += ' form-horizontal';
9953         }
9954
9955
9956         return cfg;
9957     },
9958     initEvents : function()
9959     {
9960         this.el.on('submit', this.onSubmit, this);
9961         // this was added as random key presses on the form where triggering form submit.
9962         this.el.on('keypress', function(e) {
9963             if (e.getCharCode() != 13) {
9964                 return true;
9965             }
9966             // we might need to allow it for textareas.. and some other items.
9967             // check e.getTarget().
9968
9969             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9970                 return true;
9971             }
9972
9973             Roo.log("keypress blocked");
9974
9975             e.preventDefault();
9976             return false;
9977         });
9978         
9979     },
9980     // private
9981     onSubmit : function(e){
9982         e.stopEvent();
9983     },
9984
9985      /**
9986      * Returns true if client-side validation on the form is successful.
9987      * @return Boolean
9988      */
9989     isValid : function(){
9990         var items = this.getItems();
9991         var valid = true;
9992         var target = false;
9993         
9994         items.each(function(f){
9995             
9996             if(f.validate()){
9997                 return;
9998             }
9999             
10000             Roo.log('invalid field: ' + f.name);
10001             
10002             valid = false;
10003
10004             if(!target && f.el.isVisible(true)){
10005                 target = f;
10006             }
10007            
10008         });
10009         
10010         if(this.errorMask && !valid){
10011             Roo.bootstrap.Form.popover.mask(this, target);
10012         }
10013         
10014         return valid;
10015     },
10016     
10017     /**
10018      * Returns true if any fields in this form have changed since their original load.
10019      * @return Boolean
10020      */
10021     isDirty : function(){
10022         var dirty = false;
10023         var items = this.getItems();
10024         items.each(function(f){
10025            if(f.isDirty()){
10026                dirty = true;
10027                return false;
10028            }
10029            return true;
10030         });
10031         return dirty;
10032     },
10033      /**
10034      * Performs a predefined action (submit or load) or custom actions you define on this form.
10035      * @param {String} actionName The name of the action type
10036      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10037      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10038      * accept other config options):
10039      * <pre>
10040 Property          Type             Description
10041 ----------------  ---------------  ----------------------------------------------------------------------------------
10042 url               String           The url for the action (defaults to the form's url)
10043 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10044 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10045 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10046                                    validate the form on the client (defaults to false)
10047      * </pre>
10048      * @return {BasicForm} this
10049      */
10050     doAction : function(action, options){
10051         if(typeof action == 'string'){
10052             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10053         }
10054         if(this.fireEvent('beforeaction', this, action) !== false){
10055             this.beforeAction(action);
10056             action.run.defer(100, action);
10057         }
10058         return this;
10059     },
10060
10061     // private
10062     beforeAction : function(action){
10063         var o = action.options;
10064         
10065         if(this.loadMask){
10066             
10067             if(this.maskBody){
10068                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10069             } else {
10070                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10071             }
10072         }
10073         // not really supported yet.. ??
10074
10075         //if(this.waitMsgTarget === true){
10076         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10077         //}else if(this.waitMsgTarget){
10078         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10079         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10080         //}else {
10081         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10082        // }
10083
10084     },
10085
10086     // private
10087     afterAction : function(action, success){
10088         this.activeAction = null;
10089         var o = action.options;
10090
10091         if(this.loadMask){
10092             
10093             if(this.maskBody){
10094                 Roo.get(document.body).unmask();
10095             } else {
10096                 this.el.unmask();
10097             }
10098         }
10099         
10100         //if(this.waitMsgTarget === true){
10101 //            this.el.unmask();
10102         //}else if(this.waitMsgTarget){
10103         //    this.waitMsgTarget.unmask();
10104         //}else{
10105         //    Roo.MessageBox.updateProgress(1);
10106         //    Roo.MessageBox.hide();
10107        // }
10108         //
10109         if(success){
10110             if(o.reset){
10111                 this.reset();
10112             }
10113             Roo.callback(o.success, o.scope, [this, action]);
10114             this.fireEvent('actioncomplete', this, action);
10115
10116         }else{
10117
10118             // failure condition..
10119             // we have a scenario where updates need confirming.
10120             // eg. if a locking scenario exists..
10121             // we look for { errors : { needs_confirm : true }} in the response.
10122             if (
10123                 (typeof(action.result) != 'undefined')  &&
10124                 (typeof(action.result.errors) != 'undefined')  &&
10125                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10126            ){
10127                 var _t = this;
10128                 Roo.log("not supported yet");
10129                  /*
10130
10131                 Roo.MessageBox.confirm(
10132                     "Change requires confirmation",
10133                     action.result.errorMsg,
10134                     function(r) {
10135                         if (r != 'yes') {
10136                             return;
10137                         }
10138                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10139                     }
10140
10141                 );
10142                 */
10143
10144
10145                 return;
10146             }
10147
10148             Roo.callback(o.failure, o.scope, [this, action]);
10149             // show an error message if no failed handler is set..
10150             if (!this.hasListener('actionfailed')) {
10151                 Roo.log("need to add dialog support");
10152                 /*
10153                 Roo.MessageBox.alert("Error",
10154                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10155                         action.result.errorMsg :
10156                         "Saving Failed, please check your entries or try again"
10157                 );
10158                 */
10159             }
10160
10161             this.fireEvent('actionfailed', this, action);
10162         }
10163
10164     },
10165     /**
10166      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10167      * @param {String} id The value to search for
10168      * @return Field
10169      */
10170     findField : function(id){
10171         var items = this.getItems();
10172         var field = items.get(id);
10173         if(!field){
10174              items.each(function(f){
10175                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10176                     field = f;
10177                     return false;
10178                 }
10179                 return true;
10180             });
10181         }
10182         return field || null;
10183     },
10184      /**
10185      * Mark fields in this form invalid in bulk.
10186      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10187      * @return {BasicForm} this
10188      */
10189     markInvalid : function(errors){
10190         if(errors instanceof Array){
10191             for(var i = 0, len = errors.length; i < len; i++){
10192                 var fieldError = errors[i];
10193                 var f = this.findField(fieldError.id);
10194                 if(f){
10195                     f.markInvalid(fieldError.msg);
10196                 }
10197             }
10198         }else{
10199             var field, id;
10200             for(id in errors){
10201                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10202                     field.markInvalid(errors[id]);
10203                 }
10204             }
10205         }
10206         //Roo.each(this.childForms || [], function (f) {
10207         //    f.markInvalid(errors);
10208         //});
10209
10210         return this;
10211     },
10212
10213     /**
10214      * Set values for fields in this form in bulk.
10215      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10216      * @return {BasicForm} this
10217      */
10218     setValues : function(values){
10219         if(values instanceof Array){ // array of objects
10220             for(var i = 0, len = values.length; i < len; i++){
10221                 var v = values[i];
10222                 var f = this.findField(v.id);
10223                 if(f){
10224                     f.setValue(v.value);
10225                     if(this.trackResetOnLoad){
10226                         f.originalValue = f.getValue();
10227                     }
10228                 }
10229             }
10230         }else{ // object hash
10231             var field, id;
10232             for(id in values){
10233                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10234
10235                     if (field.setFromData &&
10236                         field.valueField &&
10237                         field.displayField &&
10238                         // combos' with local stores can
10239                         // be queried via setValue()
10240                         // to set their value..
10241                         (field.store && !field.store.isLocal)
10242                         ) {
10243                         // it's a combo
10244                         var sd = { };
10245                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10246                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10247                         field.setFromData(sd);
10248
10249                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10250                         
10251                         field.setFromData(values);
10252                         
10253                     } else {
10254                         field.setValue(values[id]);
10255                     }
10256
10257
10258                     if(this.trackResetOnLoad){
10259                         field.originalValue = field.getValue();
10260                     }
10261                 }
10262             }
10263         }
10264
10265         //Roo.each(this.childForms || [], function (f) {
10266         //    f.setValues(values);
10267         //});
10268
10269         return this;
10270     },
10271
10272     /**
10273      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10274      * they are returned as an array.
10275      * @param {Boolean} asString
10276      * @return {Object}
10277      */
10278     getValues : function(asString){
10279         //if (this.childForms) {
10280             // copy values from the child forms
10281         //    Roo.each(this.childForms, function (f) {
10282         //        this.setValues(f.getValues());
10283         //    }, this);
10284         //}
10285
10286
10287
10288         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10289         if(asString === true){
10290             return fs;
10291         }
10292         return Roo.urlDecode(fs);
10293     },
10294
10295     /**
10296      * Returns the fields in this form as an object with key/value pairs.
10297      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10298      * @return {Object}
10299      */
10300     getFieldValues : function(with_hidden)
10301     {
10302         var items = this.getItems();
10303         var ret = {};
10304         items.each(function(f){
10305             
10306             if (!f.getName()) {
10307                 return;
10308             }
10309             
10310             var v = f.getValue();
10311             
10312             if (f.inputType =='radio') {
10313                 if (typeof(ret[f.getName()]) == 'undefined') {
10314                     ret[f.getName()] = ''; // empty..
10315                 }
10316
10317                 if (!f.el.dom.checked) {
10318                     return;
10319
10320                 }
10321                 v = f.el.dom.value;
10322
10323             }
10324             
10325             if(f.xtype == 'MoneyField'){
10326                 ret[f.currencyName] = f.getCurrency();
10327             }
10328
10329             // not sure if this supported any more..
10330             if ((typeof(v) == 'object') && f.getRawValue) {
10331                 v = f.getRawValue() ; // dates..
10332             }
10333             // combo boxes where name != hiddenName...
10334             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10335                 ret[f.name] = f.getRawValue();
10336             }
10337             ret[f.getName()] = v;
10338         });
10339
10340         return ret;
10341     },
10342
10343     /**
10344      * Clears all invalid messages in this form.
10345      * @return {BasicForm} this
10346      */
10347     clearInvalid : function(){
10348         var items = this.getItems();
10349
10350         items.each(function(f){
10351            f.clearInvalid();
10352         });
10353
10354         return this;
10355     },
10356
10357     /**
10358      * Resets this form.
10359      * @return {BasicForm} this
10360      */
10361     reset : function(){
10362         var items = this.getItems();
10363         items.each(function(f){
10364             f.reset();
10365         });
10366
10367         Roo.each(this.childForms || [], function (f) {
10368             f.reset();
10369         });
10370
10371
10372         return this;
10373     },
10374     
10375     getItems : function()
10376     {
10377         var r=new Roo.util.MixedCollection(false, function(o){
10378             return o.id || (o.id = Roo.id());
10379         });
10380         var iter = function(el) {
10381             if (el.inputEl) {
10382                 r.add(el);
10383             }
10384             if (!el.items) {
10385                 return;
10386             }
10387             Roo.each(el.items,function(e) {
10388                 iter(e);
10389             });
10390         };
10391
10392         iter(this);
10393         return r;
10394     },
10395     
10396     hideFields : function(items)
10397     {
10398         Roo.each(items, function(i){
10399             
10400             var f = this.findField(i);
10401             
10402             if(!f){
10403                 return;
10404             }
10405             
10406             f.hide();
10407             
10408         }, this);
10409     },
10410     
10411     showFields : function(items)
10412     {
10413         Roo.each(items, function(i){
10414             
10415             var f = this.findField(i);
10416             
10417             if(!f){
10418                 return;
10419             }
10420             
10421             f.show();
10422             
10423         }, this);
10424     }
10425
10426 });
10427
10428 Roo.apply(Roo.bootstrap.Form, {
10429     
10430     popover : {
10431         
10432         padding : 5,
10433         
10434         isApplied : false,
10435         
10436         isMasked : false,
10437         
10438         form : false,
10439         
10440         target : false,
10441         
10442         toolTip : false,
10443         
10444         intervalID : false,
10445         
10446         maskEl : false,
10447         
10448         apply : function()
10449         {
10450             if(this.isApplied){
10451                 return;
10452             }
10453             
10454             this.maskEl = {
10455                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10456                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10457                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10458                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10459             };
10460             
10461             this.maskEl.top.enableDisplayMode("block");
10462             this.maskEl.left.enableDisplayMode("block");
10463             this.maskEl.bottom.enableDisplayMode("block");
10464             this.maskEl.right.enableDisplayMode("block");
10465             
10466             this.toolTip = new Roo.bootstrap.Tooltip({
10467                 cls : 'roo-form-error-popover',
10468                 alignment : {
10469                     'left' : ['r-l', [-2,0], 'right'],
10470                     'right' : ['l-r', [2,0], 'left'],
10471                     'bottom' : ['tl-bl', [0,2], 'top'],
10472                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10473                 }
10474             });
10475             
10476             this.toolTip.render(Roo.get(document.body));
10477
10478             this.toolTip.el.enableDisplayMode("block");
10479             
10480             Roo.get(document.body).on('click', function(){
10481                 this.unmask();
10482             }, this);
10483             
10484             Roo.get(document.body).on('touchstart', function(){
10485                 this.unmask();
10486             }, this);
10487             
10488             this.isApplied = true
10489         },
10490         
10491         mask : function(form, target)
10492         {
10493             this.form = form;
10494             
10495             this.target = target;
10496             
10497             if(!this.form.errorMask || !target.el){
10498                 return;
10499             }
10500             
10501             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10502             
10503             Roo.log(scrollable);
10504             
10505             var ot = this.target.el.calcOffsetsTo(scrollable);
10506             
10507             var scrollTo = ot[1] - this.form.maskOffset;
10508             
10509             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10510             
10511             scrollable.scrollTo('top', scrollTo);
10512             
10513             var box = this.target.el.getBox();
10514             Roo.log(box);
10515             var zIndex = Roo.bootstrap.Modal.zIndex++;
10516
10517             
10518             this.maskEl.top.setStyle('position', 'absolute');
10519             this.maskEl.top.setStyle('z-index', zIndex);
10520             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10521             this.maskEl.top.setLeft(0);
10522             this.maskEl.top.setTop(0);
10523             this.maskEl.top.show();
10524             
10525             this.maskEl.left.setStyle('position', 'absolute');
10526             this.maskEl.left.setStyle('z-index', zIndex);
10527             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10528             this.maskEl.left.setLeft(0);
10529             this.maskEl.left.setTop(box.y - this.padding);
10530             this.maskEl.left.show();
10531
10532             this.maskEl.bottom.setStyle('position', 'absolute');
10533             this.maskEl.bottom.setStyle('z-index', zIndex);
10534             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10535             this.maskEl.bottom.setLeft(0);
10536             this.maskEl.bottom.setTop(box.bottom + this.padding);
10537             this.maskEl.bottom.show();
10538
10539             this.maskEl.right.setStyle('position', 'absolute');
10540             this.maskEl.right.setStyle('z-index', zIndex);
10541             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10542             this.maskEl.right.setLeft(box.right + this.padding);
10543             this.maskEl.right.setTop(box.y - this.padding);
10544             this.maskEl.right.show();
10545
10546             this.toolTip.bindEl = this.target.el;
10547
10548             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10549
10550             var tip = this.target.blankText;
10551
10552             if(this.target.getValue() !== '' ) {
10553                 
10554                 if (this.target.invalidText.length) {
10555                     tip = this.target.invalidText;
10556                 } else if (this.target.regexText.length){
10557                     tip = this.target.regexText;
10558                 }
10559             }
10560
10561             this.toolTip.show(tip);
10562
10563             this.intervalID = window.setInterval(function() {
10564                 Roo.bootstrap.Form.popover.unmask();
10565             }, 10000);
10566
10567             window.onwheel = function(){ return false;};
10568             
10569             (function(){ this.isMasked = true; }).defer(500, this);
10570             
10571         },
10572         
10573         unmask : function()
10574         {
10575             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10576                 return;
10577             }
10578             
10579             this.maskEl.top.setStyle('position', 'absolute');
10580             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10581             this.maskEl.top.hide();
10582
10583             this.maskEl.left.setStyle('position', 'absolute');
10584             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10585             this.maskEl.left.hide();
10586
10587             this.maskEl.bottom.setStyle('position', 'absolute');
10588             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10589             this.maskEl.bottom.hide();
10590
10591             this.maskEl.right.setStyle('position', 'absolute');
10592             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10593             this.maskEl.right.hide();
10594             
10595             this.toolTip.hide();
10596             
10597             this.toolTip.el.hide();
10598             
10599             window.onwheel = function(){ return true;};
10600             
10601             if(this.intervalID){
10602                 window.clearInterval(this.intervalID);
10603                 this.intervalID = false;
10604             }
10605             
10606             this.isMasked = false;
10607             
10608         }
10609         
10610     }
10611     
10612 });
10613
10614 /*
10615  * Based on:
10616  * Ext JS Library 1.1.1
10617  * Copyright(c) 2006-2007, Ext JS, LLC.
10618  *
10619  * Originally Released Under LGPL - original licence link has changed is not relivant.
10620  *
10621  * Fork - LGPL
10622  * <script type="text/javascript">
10623  */
10624 /**
10625  * @class Roo.form.VTypes
10626  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10627  * @singleton
10628  */
10629 Roo.form.VTypes = function(){
10630     // closure these in so they are only created once.
10631     var alpha = /^[a-zA-Z_]+$/;
10632     var alphanum = /^[a-zA-Z0-9_]+$/;
10633     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10634     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10635
10636     // All these messages and functions are configurable
10637     return {
10638         /**
10639          * The function used to validate email addresses
10640          * @param {String} value The email address
10641          */
10642         'email' : function(v){
10643             return email.test(v);
10644         },
10645         /**
10646          * The error text to display when the email validation function returns false
10647          * @type String
10648          */
10649         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10650         /**
10651          * The keystroke filter mask to be applied on email input
10652          * @type RegExp
10653          */
10654         'emailMask' : /[a-z0-9_\.\-@]/i,
10655
10656         /**
10657          * The function used to validate URLs
10658          * @param {String} value The URL
10659          */
10660         'url' : function(v){
10661             return url.test(v);
10662         },
10663         /**
10664          * The error text to display when the url validation function returns false
10665          * @type String
10666          */
10667         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10668         
10669         /**
10670          * The function used to validate alpha values
10671          * @param {String} value The value
10672          */
10673         'alpha' : function(v){
10674             return alpha.test(v);
10675         },
10676         /**
10677          * The error text to display when the alpha validation function returns false
10678          * @type String
10679          */
10680         'alphaText' : 'This field should only contain letters and _',
10681         /**
10682          * The keystroke filter mask to be applied on alpha input
10683          * @type RegExp
10684          */
10685         'alphaMask' : /[a-z_]/i,
10686
10687         /**
10688          * The function used to validate alphanumeric values
10689          * @param {String} value The value
10690          */
10691         'alphanum' : function(v){
10692             return alphanum.test(v);
10693         },
10694         /**
10695          * The error text to display when the alphanumeric validation function returns false
10696          * @type String
10697          */
10698         'alphanumText' : 'This field should only contain letters, numbers and _',
10699         /**
10700          * The keystroke filter mask to be applied on alphanumeric input
10701          * @type RegExp
10702          */
10703         'alphanumMask' : /[a-z0-9_]/i
10704     };
10705 }();/*
10706  * - LGPL
10707  *
10708  * Input
10709  * 
10710  */
10711
10712 /**
10713  * @class Roo.bootstrap.Input
10714  * @extends Roo.bootstrap.Component
10715  * Bootstrap Input class
10716  * @cfg {Boolean} disabled is it disabled
10717  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10718  * @cfg {String} name name of the input
10719  * @cfg {string} fieldLabel - the label associated
10720  * @cfg {string} placeholder - placeholder to put in text.
10721  * @cfg {string}  before - input group add on before
10722  * @cfg {string} after - input group add on after
10723  * @cfg {string} size - (lg|sm) or leave empty..
10724  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10725  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10726  * @cfg {Number} md colspan out of 12 for computer-sized screens
10727  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10728  * @cfg {string} value default value of the input
10729  * @cfg {Number} labelWidth set the width of label 
10730  * @cfg {Number} labellg set the width of label (1-12)
10731  * @cfg {Number} labelmd set the width of label (1-12)
10732  * @cfg {Number} labelsm set the width of label (1-12)
10733  * @cfg {Number} labelxs set the width of label (1-12)
10734  * @cfg {String} labelAlign (top|left)
10735  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10736  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10737  * @cfg {String} indicatorpos (left|right) default left
10738  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10739  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10740  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10741
10742  * @cfg {String} align (left|center|right) Default left
10743  * @cfg {Boolean} forceFeedback (true|false) Default false
10744  * 
10745  * @constructor
10746  * Create a new Input
10747  * @param {Object} config The config object
10748  */
10749
10750 Roo.bootstrap.Input = function(config){
10751     
10752     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10753     
10754     this.addEvents({
10755         /**
10756          * @event focus
10757          * Fires when this field receives input focus.
10758          * @param {Roo.form.Field} this
10759          */
10760         focus : true,
10761         /**
10762          * @event blur
10763          * Fires when this field loses input focus.
10764          * @param {Roo.form.Field} this
10765          */
10766         blur : true,
10767         /**
10768          * @event specialkey
10769          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10770          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10771          * @param {Roo.form.Field} this
10772          * @param {Roo.EventObject} e The event object
10773          */
10774         specialkey : true,
10775         /**
10776          * @event change
10777          * Fires just before the field blurs if the field value has changed.
10778          * @param {Roo.form.Field} this
10779          * @param {Mixed} newValue The new value
10780          * @param {Mixed} oldValue The original value
10781          */
10782         change : true,
10783         /**
10784          * @event invalid
10785          * Fires after the field has been marked as invalid.
10786          * @param {Roo.form.Field} this
10787          * @param {String} msg The validation message
10788          */
10789         invalid : true,
10790         /**
10791          * @event valid
10792          * Fires after the field has been validated with no errors.
10793          * @param {Roo.form.Field} this
10794          */
10795         valid : true,
10796          /**
10797          * @event keyup
10798          * Fires after the key up
10799          * @param {Roo.form.Field} this
10800          * @param {Roo.EventObject}  e The event Object
10801          */
10802         keyup : true,
10803         /**
10804          * @event paste
10805          * Fires after the user pastes into input
10806          * @param {Roo.form.Field} this
10807          * @param {Roo.EventObject}  e The event Object
10808          */
10809         paste : true
10810     });
10811 };
10812
10813 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10814      /**
10815      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10816       automatic validation (defaults to "keyup").
10817      */
10818     validationEvent : "keyup",
10819      /**
10820      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10821      */
10822     validateOnBlur : true,
10823     /**
10824      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10825      */
10826     validationDelay : 250,
10827      /**
10828      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10829      */
10830     focusClass : "x-form-focus",  // not needed???
10831     
10832        
10833     /**
10834      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10835      */
10836     invalidClass : "has-warning",
10837     
10838     /**
10839      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10840      */
10841     validClass : "has-success",
10842     
10843     /**
10844      * @cfg {Boolean} hasFeedback (true|false) default true
10845      */
10846     hasFeedback : true,
10847     
10848     /**
10849      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10850      */
10851     invalidFeedbackClass : "glyphicon-warning-sign",
10852     
10853     /**
10854      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10855      */
10856     validFeedbackClass : "glyphicon-ok",
10857     
10858     /**
10859      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10860      */
10861     selectOnFocus : false,
10862     
10863      /**
10864      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10865      */
10866     maskRe : null,
10867        /**
10868      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10869      */
10870     vtype : null,
10871     
10872       /**
10873      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10874      */
10875     disableKeyFilter : false,
10876     
10877        /**
10878      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10879      */
10880     disabled : false,
10881      /**
10882      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10883      */
10884     allowBlank : true,
10885     /**
10886      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10887      */
10888     blankText : "Please complete this mandatory field",
10889     
10890      /**
10891      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10892      */
10893     minLength : 0,
10894     /**
10895      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10896      */
10897     maxLength : Number.MAX_VALUE,
10898     /**
10899      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10900      */
10901     minLengthText : "The minimum length for this field is {0}",
10902     /**
10903      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10904      */
10905     maxLengthText : "The maximum length for this field is {0}",
10906   
10907     
10908     /**
10909      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10910      * If available, this function will be called only after the basic validators all return true, and will be passed the
10911      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10912      */
10913     validator : null,
10914     /**
10915      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10916      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10917      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10918      */
10919     regex : null,
10920     /**
10921      * @cfg {String} regexText -- Depricated - use Invalid Text
10922      */
10923     regexText : "",
10924     
10925     /**
10926      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10927      */
10928     invalidText : "",
10929     
10930     
10931     
10932     autocomplete: false,
10933     
10934     
10935     fieldLabel : '',
10936     inputType : 'text',
10937     
10938     name : false,
10939     placeholder: false,
10940     before : false,
10941     after : false,
10942     size : false,
10943     hasFocus : false,
10944     preventMark: false,
10945     isFormField : true,
10946     value : '',
10947     labelWidth : 2,
10948     labelAlign : false,
10949     readOnly : false,
10950     align : false,
10951     formatedValue : false,
10952     forceFeedback : false,
10953     
10954     indicatorpos : 'left',
10955     
10956     labellg : 0,
10957     labelmd : 0,
10958     labelsm : 0,
10959     labelxs : 0,
10960     
10961     capture : '',
10962     accept : '',
10963     
10964     parentLabelAlign : function()
10965     {
10966         var parent = this;
10967         while (parent.parent()) {
10968             parent = parent.parent();
10969             if (typeof(parent.labelAlign) !='undefined') {
10970                 return parent.labelAlign;
10971             }
10972         }
10973         return 'left';
10974         
10975     },
10976     
10977     getAutoCreate : function()
10978     {
10979         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10980         
10981         var id = Roo.id();
10982         
10983         var cfg = {};
10984         
10985         if(this.inputType != 'hidden'){
10986             cfg.cls = 'form-group' //input-group
10987         }
10988         
10989         var input =  {
10990             tag: 'input',
10991             id : id,
10992             type : this.inputType,
10993             value : this.value,
10994             cls : 'form-control',
10995             placeholder : this.placeholder || '',
10996             autocomplete : this.autocomplete || 'new-password'
10997         };
10998         if (this.inputType == 'file') {
10999             input.style = 'overflow:hidden'; // why not in CSS?
11000         }
11001         
11002         if(this.capture.length){
11003             input.capture = this.capture;
11004         }
11005         
11006         if(this.accept.length){
11007             input.accept = this.accept + "/*";
11008         }
11009         
11010         if(this.align){
11011             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11012         }
11013         
11014         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11015             input.maxLength = this.maxLength;
11016         }
11017         
11018         if (this.disabled) {
11019             input.disabled=true;
11020         }
11021         
11022         if (this.readOnly) {
11023             input.readonly=true;
11024         }
11025         
11026         if (this.name) {
11027             input.name = this.name;
11028         }
11029         
11030         if (this.size) {
11031             input.cls += ' input-' + this.size;
11032         }
11033         
11034         var settings=this;
11035         ['xs','sm','md','lg'].map(function(size){
11036             if (settings[size]) {
11037                 cfg.cls += ' col-' + size + '-' + settings[size];
11038             }
11039         });
11040         
11041         var inputblock = input;
11042         
11043         var feedback = {
11044             tag: 'span',
11045             cls: 'glyphicon form-control-feedback'
11046         };
11047             
11048         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11049             
11050             inputblock = {
11051                 cls : 'has-feedback',
11052                 cn :  [
11053                     input,
11054                     feedback
11055                 ] 
11056             };  
11057         }
11058         
11059         if (this.before || this.after) {
11060             
11061             inputblock = {
11062                 cls : 'input-group',
11063                 cn :  [] 
11064             };
11065             
11066             if (this.before && typeof(this.before) == 'string') {
11067                 
11068                 inputblock.cn.push({
11069                     tag :'span',
11070                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11071                     html : this.before
11072                 });
11073             }
11074             if (this.before && typeof(this.before) == 'object') {
11075                 this.before = Roo.factory(this.before);
11076                 
11077                 inputblock.cn.push({
11078                     tag :'span',
11079                     cls : 'roo-input-before input-group-prepend   input-group-' +
11080                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11081                 });
11082             }
11083             
11084             inputblock.cn.push(input);
11085             
11086             if (this.after && typeof(this.after) == 'string') {
11087                 inputblock.cn.push({
11088                     tag :'span',
11089                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11090                     html : this.after
11091                 });
11092             }
11093             if (this.after && typeof(this.after) == 'object') {
11094                 this.after = Roo.factory(this.after);
11095                 
11096                 inputblock.cn.push({
11097                     tag :'span',
11098                     cls : 'roo-input-after input-group-append  input-group-' +
11099                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11100                 });
11101             }
11102             
11103             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11104                 inputblock.cls += ' has-feedback';
11105                 inputblock.cn.push(feedback);
11106             }
11107         };
11108         var indicator = {
11109             tag : 'i',
11110             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11111             tooltip : 'This field is required'
11112         };
11113         if (this.allowBlank ) {
11114             indicator.style = this.allowBlank ? ' display:none' : '';
11115         }
11116         if (align ==='left' && this.fieldLabel.length) {
11117             
11118             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11119             
11120             cfg.cn = [
11121                 indicator,
11122                 {
11123                     tag: 'label',
11124                     'for' :  id,
11125                     cls : 'control-label col-form-label',
11126                     html : this.fieldLabel
11127
11128                 },
11129                 {
11130                     cls : "", 
11131                     cn: [
11132                         inputblock
11133                     ]
11134                 }
11135             ];
11136             
11137             var labelCfg = cfg.cn[1];
11138             var contentCfg = cfg.cn[2];
11139             
11140             if(this.indicatorpos == 'right'){
11141                 cfg.cn = [
11142                     {
11143                         tag: 'label',
11144                         'for' :  id,
11145                         cls : 'control-label col-form-label',
11146                         cn : [
11147                             {
11148                                 tag : 'span',
11149                                 html : this.fieldLabel
11150                             },
11151                             indicator
11152                         ]
11153                     },
11154                     {
11155                         cls : "",
11156                         cn: [
11157                             inputblock
11158                         ]
11159                     }
11160
11161                 ];
11162                 
11163                 labelCfg = cfg.cn[0];
11164                 contentCfg = cfg.cn[1];
11165             
11166             }
11167             
11168             if(this.labelWidth > 12){
11169                 labelCfg.style = "width: " + this.labelWidth + 'px';
11170             }
11171             
11172             if(this.labelWidth < 13 && this.labelmd == 0){
11173                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11174             }
11175             
11176             if(this.labellg > 0){
11177                 labelCfg.cls += ' col-lg-' + this.labellg;
11178                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11179             }
11180             
11181             if(this.labelmd > 0){
11182                 labelCfg.cls += ' col-md-' + this.labelmd;
11183                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11184             }
11185             
11186             if(this.labelsm > 0){
11187                 labelCfg.cls += ' col-sm-' + this.labelsm;
11188                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11189             }
11190             
11191             if(this.labelxs > 0){
11192                 labelCfg.cls += ' col-xs-' + this.labelxs;
11193                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11194             }
11195             
11196             
11197         } else if ( this.fieldLabel.length) {
11198                 
11199             
11200             
11201             cfg.cn = [
11202                 {
11203                     tag : 'i',
11204                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11205                     tooltip : 'This field is required',
11206                     style : this.allowBlank ? ' display:none' : '' 
11207                 },
11208                 {
11209                     tag: 'label',
11210                    //cls : 'input-group-addon',
11211                     html : this.fieldLabel
11212
11213                 },
11214
11215                inputblock
11216
11217            ];
11218            
11219            if(this.indicatorpos == 'right'){
11220        
11221                 cfg.cn = [
11222                     {
11223                         tag: 'label',
11224                        //cls : 'input-group-addon',
11225                         html : this.fieldLabel
11226
11227                     },
11228                     {
11229                         tag : 'i',
11230                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11231                         tooltip : 'This field is required',
11232                         style : this.allowBlank ? ' display:none' : '' 
11233                     },
11234
11235                    inputblock
11236
11237                ];
11238
11239             }
11240
11241         } else {
11242             
11243             cfg.cn = [
11244
11245                     inputblock
11246
11247             ];
11248                 
11249                 
11250         };
11251         
11252         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11253            cfg.cls += ' navbar-form';
11254         }
11255         
11256         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11257             // on BS4 we do this only if not form 
11258             cfg.cls += ' navbar-form';
11259             cfg.tag = 'li';
11260         }
11261         
11262         return cfg;
11263         
11264     },
11265     /**
11266      * return the real input element.
11267      */
11268     inputEl: function ()
11269     {
11270         return this.el.select('input.form-control',true).first();
11271     },
11272     
11273     tooltipEl : function()
11274     {
11275         return this.inputEl();
11276     },
11277     
11278     indicatorEl : function()
11279     {
11280         if (Roo.bootstrap.version == 4) {
11281             return false; // not enabled in v4 yet.
11282         }
11283         
11284         var indicator = this.el.select('i.roo-required-indicator',true).first();
11285         
11286         if(!indicator){
11287             return false;
11288         }
11289         
11290         return indicator;
11291         
11292     },
11293     
11294     setDisabled : function(v)
11295     {
11296         var i  = this.inputEl().dom;
11297         if (!v) {
11298             i.removeAttribute('disabled');
11299             return;
11300             
11301         }
11302         i.setAttribute('disabled','true');
11303     },
11304     initEvents : function()
11305     {
11306           
11307         this.inputEl().on("keydown" , this.fireKey,  this);
11308         this.inputEl().on("focus", this.onFocus,  this);
11309         this.inputEl().on("blur", this.onBlur,  this);
11310         
11311         this.inputEl().relayEvent('keyup', this);
11312         this.inputEl().relayEvent('paste', this);
11313         
11314         this.indicator = this.indicatorEl();
11315         
11316         if(this.indicator){
11317             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11318         }
11319  
11320         // reference to original value for reset
11321         this.originalValue = this.getValue();
11322         //Roo.form.TextField.superclass.initEvents.call(this);
11323         if(this.validationEvent == 'keyup'){
11324             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11325             this.inputEl().on('keyup', this.filterValidation, this);
11326         }
11327         else if(this.validationEvent !== false){
11328             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11329         }
11330         
11331         if(this.selectOnFocus){
11332             this.on("focus", this.preFocus, this);
11333             
11334         }
11335         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11336             this.inputEl().on("keypress", this.filterKeys, this);
11337         } else {
11338             this.inputEl().relayEvent('keypress', this);
11339         }
11340        /* if(this.grow){
11341             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11342             this.el.on("click", this.autoSize,  this);
11343         }
11344         */
11345         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11346             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11347         }
11348         
11349         if (typeof(this.before) == 'object') {
11350             this.before.render(this.el.select('.roo-input-before',true).first());
11351         }
11352         if (typeof(this.after) == 'object') {
11353             this.after.render(this.el.select('.roo-input-after',true).first());
11354         }
11355         
11356         this.inputEl().on('change', this.onChange, this);
11357         
11358     },
11359     filterValidation : function(e){
11360         if(!e.isNavKeyPress()){
11361             this.validationTask.delay(this.validationDelay);
11362         }
11363     },
11364      /**
11365      * Validates the field value
11366      * @return {Boolean} True if the value is valid, else false
11367      */
11368     validate : function(){
11369         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11370         if(this.disabled || this.validateValue(this.getRawValue())){
11371             this.markValid();
11372             return true;
11373         }
11374         
11375         this.markInvalid();
11376         return false;
11377     },
11378     
11379     
11380     /**
11381      * Validates a value according to the field's validation rules and marks the field as invalid
11382      * if the validation fails
11383      * @param {Mixed} value The value to validate
11384      * @return {Boolean} True if the value is valid, else false
11385      */
11386     validateValue : function(value)
11387     {
11388         if(this.getVisibilityEl().hasClass('hidden')){
11389             return true;
11390         }
11391         
11392         if(value.length < 1)  { // if it's blank
11393             if(this.allowBlank){
11394                 return true;
11395             }
11396             return false;
11397         }
11398         
11399         if(value.length < this.minLength){
11400             return false;
11401         }
11402         if(value.length > this.maxLength){
11403             return false;
11404         }
11405         if(this.vtype){
11406             var vt = Roo.form.VTypes;
11407             if(!vt[this.vtype](value, this)){
11408                 return false;
11409             }
11410         }
11411         if(typeof this.validator == "function"){
11412             var msg = this.validator(value);
11413             if(msg !== true){
11414                 return false;
11415             }
11416             if (typeof(msg) == 'string') {
11417                 this.invalidText = msg;
11418             }
11419         }
11420         
11421         if(this.regex && !this.regex.test(value)){
11422             return false;
11423         }
11424         
11425         return true;
11426     },
11427     
11428      // private
11429     fireKey : function(e){
11430         //Roo.log('field ' + e.getKey());
11431         if(e.isNavKeyPress()){
11432             this.fireEvent("specialkey", this, e);
11433         }
11434     },
11435     focus : function (selectText){
11436         if(this.rendered){
11437             this.inputEl().focus();
11438             if(selectText === true){
11439                 this.inputEl().dom.select();
11440             }
11441         }
11442         return this;
11443     } ,
11444     
11445     onFocus : function(){
11446         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11447            // this.el.addClass(this.focusClass);
11448         }
11449         if(!this.hasFocus){
11450             this.hasFocus = true;
11451             this.startValue = this.getValue();
11452             this.fireEvent("focus", this);
11453         }
11454     },
11455     
11456     beforeBlur : Roo.emptyFn,
11457
11458     
11459     // private
11460     onBlur : function(){
11461         this.beforeBlur();
11462         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11463             //this.el.removeClass(this.focusClass);
11464         }
11465         this.hasFocus = false;
11466         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11467             this.validate();
11468         }
11469         var v = this.getValue();
11470         if(String(v) !== String(this.startValue)){
11471             this.fireEvent('change', this, v, this.startValue);
11472         }
11473         this.fireEvent("blur", this);
11474     },
11475     
11476     onChange : function(e)
11477     {
11478         var v = this.getValue();
11479         if(String(v) !== String(this.startValue)){
11480             this.fireEvent('change', this, v, this.startValue);
11481         }
11482         
11483     },
11484     
11485     /**
11486      * Resets the current field value to the originally loaded value and clears any validation messages
11487      */
11488     reset : function(){
11489         this.setValue(this.originalValue);
11490         this.validate();
11491     },
11492      /**
11493      * Returns the name of the field
11494      * @return {Mixed} name The name field
11495      */
11496     getName: function(){
11497         return this.name;
11498     },
11499      /**
11500      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11501      * @return {Mixed} value The field value
11502      */
11503     getValue : function(){
11504         
11505         var v = this.inputEl().getValue();
11506         
11507         return v;
11508     },
11509     /**
11510      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11511      * @return {Mixed} value The field value
11512      */
11513     getRawValue : function(){
11514         var v = this.inputEl().getValue();
11515         
11516         return v;
11517     },
11518     
11519     /**
11520      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11521      * @param {Mixed} value The value to set
11522      */
11523     setRawValue : function(v){
11524         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11525     },
11526     
11527     selectText : function(start, end){
11528         var v = this.getRawValue();
11529         if(v.length > 0){
11530             start = start === undefined ? 0 : start;
11531             end = end === undefined ? v.length : end;
11532             var d = this.inputEl().dom;
11533             if(d.setSelectionRange){
11534                 d.setSelectionRange(start, end);
11535             }else if(d.createTextRange){
11536                 var range = d.createTextRange();
11537                 range.moveStart("character", start);
11538                 range.moveEnd("character", v.length-end);
11539                 range.select();
11540             }
11541         }
11542     },
11543     
11544     /**
11545      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11546      * @param {Mixed} value The value to set
11547      */
11548     setValue : function(v){
11549         this.value = v;
11550         if(this.rendered){
11551             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11552             this.validate();
11553         }
11554     },
11555     
11556     /*
11557     processValue : function(value){
11558         if(this.stripCharsRe){
11559             var newValue = value.replace(this.stripCharsRe, '');
11560             if(newValue !== value){
11561                 this.setRawValue(newValue);
11562                 return newValue;
11563             }
11564         }
11565         return value;
11566     },
11567   */
11568     preFocus : function(){
11569         
11570         if(this.selectOnFocus){
11571             this.inputEl().dom.select();
11572         }
11573     },
11574     filterKeys : function(e){
11575         var k = e.getKey();
11576         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11577             return;
11578         }
11579         var c = e.getCharCode(), cc = String.fromCharCode(c);
11580         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11581             return;
11582         }
11583         if(!this.maskRe.test(cc)){
11584             e.stopEvent();
11585         }
11586     },
11587      /**
11588      * Clear any invalid styles/messages for this field
11589      */
11590     clearInvalid : function(){
11591         
11592         if(!this.el || this.preventMark){ // not rendered
11593             return;
11594         }
11595         
11596         
11597         this.el.removeClass([this.invalidClass, 'is-invalid']);
11598         
11599         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11600             
11601             var feedback = this.el.select('.form-control-feedback', true).first();
11602             
11603             if(feedback){
11604                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11605             }
11606             
11607         }
11608         
11609         if(this.indicator){
11610             this.indicator.removeClass('visible');
11611             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11612         }
11613         
11614         this.fireEvent('valid', this);
11615     },
11616     
11617      /**
11618      * Mark this field as valid
11619      */
11620     markValid : function()
11621     {
11622         if(!this.el  || this.preventMark){ // not rendered...
11623             return;
11624         }
11625         
11626         this.el.removeClass([this.invalidClass, this.validClass]);
11627         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11628
11629         var feedback = this.el.select('.form-control-feedback', true).first();
11630             
11631         if(feedback){
11632             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11633         }
11634         
11635         if(this.indicator){
11636             this.indicator.removeClass('visible');
11637             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11638         }
11639         
11640         if(this.disabled){
11641             return;
11642         }
11643         
11644            
11645         if(this.allowBlank && !this.getRawValue().length){
11646             return;
11647         }
11648         if (Roo.bootstrap.version == 3) {
11649             this.el.addClass(this.validClass);
11650         } else {
11651             this.inputEl().addClass('is-valid');
11652         }
11653
11654         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11655             
11656             var feedback = this.el.select('.form-control-feedback', true).first();
11657             
11658             if(feedback){
11659                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11660                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11661             }
11662             
11663         }
11664         
11665         this.fireEvent('valid', this);
11666     },
11667     
11668      /**
11669      * Mark this field as invalid
11670      * @param {String} msg The validation message
11671      */
11672     markInvalid : function(msg)
11673     {
11674         if(!this.el  || this.preventMark){ // not rendered
11675             return;
11676         }
11677         
11678         this.el.removeClass([this.invalidClass, this.validClass]);
11679         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11680         
11681         var feedback = this.el.select('.form-control-feedback', true).first();
11682             
11683         if(feedback){
11684             this.el.select('.form-control-feedback', true).first().removeClass(
11685                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11686         }
11687
11688         if(this.disabled){
11689             return;
11690         }
11691         
11692         if(this.allowBlank && !this.getRawValue().length){
11693             return;
11694         }
11695         
11696         if(this.indicator){
11697             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11698             this.indicator.addClass('visible');
11699         }
11700         if (Roo.bootstrap.version == 3) {
11701             this.el.addClass(this.invalidClass);
11702         } else {
11703             this.inputEl().addClass('is-invalid');
11704         }
11705         
11706         
11707         
11708         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11709             
11710             var feedback = this.el.select('.form-control-feedback', true).first();
11711             
11712             if(feedback){
11713                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11714                 
11715                 if(this.getValue().length || this.forceFeedback){
11716                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11717                 }
11718                 
11719             }
11720             
11721         }
11722         
11723         this.fireEvent('invalid', this, msg);
11724     },
11725     // private
11726     SafariOnKeyDown : function(event)
11727     {
11728         // this is a workaround for a password hang bug on chrome/ webkit.
11729         if (this.inputEl().dom.type != 'password') {
11730             return;
11731         }
11732         
11733         var isSelectAll = false;
11734         
11735         if(this.inputEl().dom.selectionEnd > 0){
11736             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11737         }
11738         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11739             event.preventDefault();
11740             this.setValue('');
11741             return;
11742         }
11743         
11744         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11745             
11746             event.preventDefault();
11747             // this is very hacky as keydown always get's upper case.
11748             //
11749             var cc = String.fromCharCode(event.getCharCode());
11750             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11751             
11752         }
11753     },
11754     adjustWidth : function(tag, w){
11755         tag = tag.toLowerCase();
11756         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11757             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11758                 if(tag == 'input'){
11759                     return w + 2;
11760                 }
11761                 if(tag == 'textarea'){
11762                     return w-2;
11763                 }
11764             }else if(Roo.isOpera){
11765                 if(tag == 'input'){
11766                     return w + 2;
11767                 }
11768                 if(tag == 'textarea'){
11769                     return w-2;
11770                 }
11771             }
11772         }
11773         return w;
11774     },
11775     
11776     setFieldLabel : function(v)
11777     {
11778         if(!this.rendered){
11779             return;
11780         }
11781         
11782         if(this.indicatorEl()){
11783             var ar = this.el.select('label > span',true);
11784             
11785             if (ar.elements.length) {
11786                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11787                 this.fieldLabel = v;
11788                 return;
11789             }
11790             
11791             var br = this.el.select('label',true);
11792             
11793             if(br.elements.length) {
11794                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11795                 this.fieldLabel = v;
11796                 return;
11797             }
11798             
11799             Roo.log('Cannot Found any of label > span || label in input');
11800             return;
11801         }
11802         
11803         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804         this.fieldLabel = v;
11805         
11806         
11807     }
11808 });
11809
11810  
11811 /*
11812  * - LGPL
11813  *
11814  * Input
11815  * 
11816  */
11817
11818 /**
11819  * @class Roo.bootstrap.TextArea
11820  * @extends Roo.bootstrap.Input
11821  * Bootstrap TextArea class
11822  * @cfg {Number} cols Specifies the visible width of a text area
11823  * @cfg {Number} rows Specifies the visible number of lines in a text area
11824  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11825  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11826  * @cfg {string} html text
11827  * 
11828  * @constructor
11829  * Create a new TextArea
11830  * @param {Object} config The config object
11831  */
11832
11833 Roo.bootstrap.TextArea = function(config){
11834     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11835    
11836 };
11837
11838 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11839      
11840     cols : false,
11841     rows : 5,
11842     readOnly : false,
11843     warp : 'soft',
11844     resize : false,
11845     value: false,
11846     html: false,
11847     
11848     getAutoCreate : function(){
11849         
11850         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11851         
11852         var id = Roo.id();
11853         
11854         var cfg = {};
11855         
11856         if(this.inputType != 'hidden'){
11857             cfg.cls = 'form-group' //input-group
11858         }
11859         
11860         var input =  {
11861             tag: 'textarea',
11862             id : id,
11863             warp : this.warp,
11864             rows : this.rows,
11865             value : this.value || '',
11866             html: this.html || '',
11867             cls : 'form-control',
11868             placeholder : this.placeholder || '' 
11869             
11870         };
11871         
11872         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11873             input.maxLength = this.maxLength;
11874         }
11875         
11876         if(this.resize){
11877             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11878         }
11879         
11880         if(this.cols){
11881             input.cols = this.cols;
11882         }
11883         
11884         if (this.readOnly) {
11885             input.readonly = true;
11886         }
11887         
11888         if (this.name) {
11889             input.name = this.name;
11890         }
11891         
11892         if (this.size) {
11893             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11894         }
11895         
11896         var settings=this;
11897         ['xs','sm','md','lg'].map(function(size){
11898             if (settings[size]) {
11899                 cfg.cls += ' col-' + size + '-' + settings[size];
11900             }
11901         });
11902         
11903         var inputblock = input;
11904         
11905         if(this.hasFeedback && !this.allowBlank){
11906             
11907             var feedback = {
11908                 tag: 'span',
11909                 cls: 'glyphicon form-control-feedback'
11910             };
11911
11912             inputblock = {
11913                 cls : 'has-feedback',
11914                 cn :  [
11915                     input,
11916                     feedback
11917                 ] 
11918             };  
11919         }
11920         
11921         
11922         if (this.before || this.after) {
11923             
11924             inputblock = {
11925                 cls : 'input-group',
11926                 cn :  [] 
11927             };
11928             if (this.before) {
11929                 inputblock.cn.push({
11930                     tag :'span',
11931                     cls : 'input-group-addon',
11932                     html : this.before
11933                 });
11934             }
11935             
11936             inputblock.cn.push(input);
11937             
11938             if(this.hasFeedback && !this.allowBlank){
11939                 inputblock.cls += ' has-feedback';
11940                 inputblock.cn.push(feedback);
11941             }
11942             
11943             if (this.after) {
11944                 inputblock.cn.push({
11945                     tag :'span',
11946                     cls : 'input-group-addon',
11947                     html : this.after
11948                 });
11949             }
11950             
11951         }
11952         
11953         if (align ==='left' && this.fieldLabel.length) {
11954             cfg.cn = [
11955                 {
11956                     tag: 'label',
11957                     'for' :  id,
11958                     cls : 'control-label',
11959                     html : this.fieldLabel
11960                 },
11961                 {
11962                     cls : "",
11963                     cn: [
11964                         inputblock
11965                     ]
11966                 }
11967
11968             ];
11969             
11970             if(this.labelWidth > 12){
11971                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11972             }
11973
11974             if(this.labelWidth < 13 && this.labelmd == 0){
11975                 this.labelmd = this.labelWidth;
11976             }
11977
11978             if(this.labellg > 0){
11979                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11980                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11981             }
11982
11983             if(this.labelmd > 0){
11984                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11985                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11986             }
11987
11988             if(this.labelsm > 0){
11989                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11990                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11991             }
11992
11993             if(this.labelxs > 0){
11994                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11995                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11996             }
11997             
11998         } else if ( this.fieldLabel.length) {
11999             cfg.cn = [
12000
12001                {
12002                    tag: 'label',
12003                    //cls : 'input-group-addon',
12004                    html : this.fieldLabel
12005
12006                },
12007
12008                inputblock
12009
12010            ];
12011
12012         } else {
12013
12014             cfg.cn = [
12015
12016                 inputblock
12017
12018             ];
12019                 
12020         }
12021         
12022         if (this.disabled) {
12023             input.disabled=true;
12024         }
12025         
12026         return cfg;
12027         
12028     },
12029     /**
12030      * return the real textarea element.
12031      */
12032     inputEl: function ()
12033     {
12034         return this.el.select('textarea.form-control',true).first();
12035     },
12036     
12037     /**
12038      * Clear any invalid styles/messages for this field
12039      */
12040     clearInvalid : function()
12041     {
12042         
12043         if(!this.el || this.preventMark){ // not rendered
12044             return;
12045         }
12046         
12047         var label = this.el.select('label', true).first();
12048         var icon = this.el.select('i.fa-star', true).first();
12049         
12050         if(label && icon){
12051             icon.remove();
12052         }
12053         this.el.removeClass( this.validClass);
12054         this.inputEl().removeClass('is-invalid');
12055          
12056         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12057             
12058             var feedback = this.el.select('.form-control-feedback', true).first();
12059             
12060             if(feedback){
12061                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12062             }
12063             
12064         }
12065         
12066         this.fireEvent('valid', this);
12067     },
12068     
12069      /**
12070      * Mark this field as valid
12071      */
12072     markValid : function()
12073     {
12074         if(!this.el  || this.preventMark){ // not rendered
12075             return;
12076         }
12077         
12078         this.el.removeClass([this.invalidClass, this.validClass]);
12079         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12080         
12081         var feedback = this.el.select('.form-control-feedback', true).first();
12082             
12083         if(feedback){
12084             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12085         }
12086
12087         if(this.disabled || this.allowBlank){
12088             return;
12089         }
12090         
12091         var label = this.el.select('label', true).first();
12092         var icon = this.el.select('i.fa-star', true).first();
12093         
12094         if(label && icon){
12095             icon.remove();
12096         }
12097         if (Roo.bootstrap.version == 3) {
12098             this.el.addClass(this.validClass);
12099         } else {
12100             this.inputEl().addClass('is-valid');
12101         }
12102         
12103         
12104         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12105             
12106             var feedback = this.el.select('.form-control-feedback', true).first();
12107             
12108             if(feedback){
12109                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12110                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12111             }
12112             
12113         }
12114         
12115         this.fireEvent('valid', this);
12116     },
12117     
12118      /**
12119      * Mark this field as invalid
12120      * @param {String} msg The validation message
12121      */
12122     markInvalid : function(msg)
12123     {
12124         if(!this.el  || this.preventMark){ // not rendered
12125             return;
12126         }
12127         
12128         this.el.removeClass([this.invalidClass, this.validClass]);
12129         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12130         
12131         var feedback = this.el.select('.form-control-feedback', true).first();
12132             
12133         if(feedback){
12134             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12135         }
12136
12137         if(this.disabled || this.allowBlank){
12138             return;
12139         }
12140         
12141         var label = this.el.select('label', true).first();
12142         var icon = this.el.select('i.fa-star', true).first();
12143         
12144         if(!this.getValue().length && label && !icon){
12145             this.el.createChild({
12146                 tag : 'i',
12147                 cls : 'text-danger fa fa-lg fa-star',
12148                 tooltip : 'This field is required',
12149                 style : 'margin-right:5px;'
12150             }, label, true);
12151         }
12152         
12153         if (Roo.bootstrap.version == 3) {
12154             this.el.addClass(this.invalidClass);
12155         } else {
12156             this.inputEl().addClass('is-invalid');
12157         }
12158         
12159         // fixme ... this may be depricated need to test..
12160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12161             
12162             var feedback = this.el.select('.form-control-feedback', true).first();
12163             
12164             if(feedback){
12165                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12166                 
12167                 if(this.getValue().length || this.forceFeedback){
12168                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12169                 }
12170                 
12171             }
12172             
12173         }
12174         
12175         this.fireEvent('invalid', this, msg);
12176     }
12177 });
12178
12179  
12180 /*
12181  * - LGPL
12182  *
12183  * trigger field - base class for combo..
12184  * 
12185  */
12186  
12187 /**
12188  * @class Roo.bootstrap.TriggerField
12189  * @extends Roo.bootstrap.Input
12190  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12191  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12192  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12193  * for which you can provide a custom implementation.  For example:
12194  * <pre><code>
12195 var trigger = new Roo.bootstrap.TriggerField();
12196 trigger.onTriggerClick = myTriggerFn;
12197 trigger.applyTo('my-field');
12198 </code></pre>
12199  *
12200  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12201  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12202  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12203  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12204  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12205
12206  * @constructor
12207  * Create a new TriggerField.
12208  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12209  * to the base TextField)
12210  */
12211 Roo.bootstrap.TriggerField = function(config){
12212     this.mimicing = false;
12213     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12214 };
12215
12216 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12217     /**
12218      * @cfg {String} triggerClass A CSS class to apply to the trigger
12219      */
12220      /**
12221      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12222      */
12223     hideTrigger:false,
12224
12225     /**
12226      * @cfg {Boolean} removable (true|false) special filter default false
12227      */
12228     removable : false,
12229     
12230     /** @cfg {Boolean} grow @hide */
12231     /** @cfg {Number} growMin @hide */
12232     /** @cfg {Number} growMax @hide */
12233
12234     /**
12235      * @hide 
12236      * @method
12237      */
12238     autoSize: Roo.emptyFn,
12239     // private
12240     monitorTab : true,
12241     // private
12242     deferHeight : true,
12243
12244     
12245     actionMode : 'wrap',
12246     
12247     caret : false,
12248     
12249     
12250     getAutoCreate : function(){
12251        
12252         var align = this.labelAlign || this.parentLabelAlign();
12253         
12254         var id = Roo.id();
12255         
12256         var cfg = {
12257             cls: 'form-group' //input-group
12258         };
12259         
12260         
12261         var input =  {
12262             tag: 'input',
12263             id : id,
12264             type : this.inputType,
12265             cls : 'form-control',
12266             autocomplete: 'new-password',
12267             placeholder : this.placeholder || '' 
12268             
12269         };
12270         if (this.name) {
12271             input.name = this.name;
12272         }
12273         if (this.size) {
12274             input.cls += ' input-' + this.size;
12275         }
12276         
12277         if (this.disabled) {
12278             input.disabled=true;
12279         }
12280         
12281         var inputblock = input;
12282         
12283         if(this.hasFeedback && !this.allowBlank){
12284             
12285             var feedback = {
12286                 tag: 'span',
12287                 cls: 'glyphicon form-control-feedback'
12288             };
12289             
12290             if(this.removable && !this.editable  ){
12291                 inputblock = {
12292                     cls : 'has-feedback',
12293                     cn :  [
12294                         inputblock,
12295                         {
12296                             tag: 'button',
12297                             html : 'x',
12298                             cls : 'roo-combo-removable-btn close'
12299                         },
12300                         feedback
12301                     ] 
12302                 };
12303             } else {
12304                 inputblock = {
12305                     cls : 'has-feedback',
12306                     cn :  [
12307                         inputblock,
12308                         feedback
12309                     ] 
12310                 };
12311             }
12312
12313         } else {
12314             if(this.removable && !this.editable ){
12315                 inputblock = {
12316                     cls : 'roo-removable',
12317                     cn :  [
12318                         inputblock,
12319                         {
12320                             tag: 'button',
12321                             html : 'x',
12322                             cls : 'roo-combo-removable-btn close'
12323                         }
12324                     ] 
12325                 };
12326             }
12327         }
12328         
12329         if (this.before || this.after) {
12330             
12331             inputblock = {
12332                 cls : 'input-group',
12333                 cn :  [] 
12334             };
12335             if (this.before) {
12336                 inputblock.cn.push({
12337                     tag :'span',
12338                     cls : 'input-group-addon input-group-prepend input-group-text',
12339                     html : this.before
12340                 });
12341             }
12342             
12343             inputblock.cn.push(input);
12344             
12345             if(this.hasFeedback && !this.allowBlank){
12346                 inputblock.cls += ' has-feedback';
12347                 inputblock.cn.push(feedback);
12348             }
12349             
12350             if (this.after) {
12351                 inputblock.cn.push({
12352                     tag :'span',
12353                     cls : 'input-group-addon input-group-append input-group-text',
12354                     html : this.after
12355                 });
12356             }
12357             
12358         };
12359         
12360       
12361         
12362         var ibwrap = inputblock;
12363         
12364         if(this.multiple){
12365             ibwrap = {
12366                 tag: 'ul',
12367                 cls: 'roo-select2-choices',
12368                 cn:[
12369                     {
12370                         tag: 'li',
12371                         cls: 'roo-select2-search-field',
12372                         cn: [
12373
12374                             inputblock
12375                         ]
12376                     }
12377                 ]
12378             };
12379                 
12380         }
12381         
12382         var combobox = {
12383             cls: 'roo-select2-container input-group',
12384             cn: [
12385                  {
12386                     tag: 'input',
12387                     type : 'hidden',
12388                     cls: 'form-hidden-field'
12389                 },
12390                 ibwrap
12391             ]
12392         };
12393         
12394         if(!this.multiple && this.showToggleBtn){
12395             
12396             var caret = {
12397                         tag: 'span',
12398                         cls: 'caret'
12399              };
12400             if (this.caret != false) {
12401                 caret = {
12402                      tag: 'i',
12403                      cls: 'fa fa-' + this.caret
12404                 };
12405                 
12406             }
12407             
12408             combobox.cn.push({
12409                 tag :'span',
12410                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12411                 cn : [
12412                     Roo.bootstrap.version == 3 ? caret : '',
12413                     {
12414                         tag: 'span',
12415                         cls: 'combobox-clear',
12416                         cn  : [
12417                             {
12418                                 tag : 'i',
12419                                 cls: 'icon-remove'
12420                             }
12421                         ]
12422                     }
12423                 ]
12424
12425             })
12426         }
12427         
12428         if(this.multiple){
12429             combobox.cls += ' roo-select2-container-multi';
12430         }
12431          var indicator = {
12432             tag : 'i',
12433             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12434             tooltip : 'This field is required'
12435         };
12436         if (Roo.bootstrap.version == 4) {
12437             indicator = {
12438                 tag : 'i',
12439                 style : 'display:none'
12440             };
12441         }
12442         
12443         
12444         if (align ==='left' && this.fieldLabel.length) {
12445             
12446             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12447
12448             cfg.cn = [
12449                 indicator,
12450                 {
12451                     tag: 'label',
12452                     'for' :  id,
12453                     cls : 'control-label',
12454                     html : this.fieldLabel
12455
12456                 },
12457                 {
12458                     cls : "", 
12459                     cn: [
12460                         combobox
12461                     ]
12462                 }
12463
12464             ];
12465             
12466             var labelCfg = cfg.cn[1];
12467             var contentCfg = cfg.cn[2];
12468             
12469             if(this.indicatorpos == 'right'){
12470                 cfg.cn = [
12471                     {
12472                         tag: 'label',
12473                         'for' :  id,
12474                         cls : 'control-label',
12475                         cn : [
12476                             {
12477                                 tag : 'span',
12478                                 html : this.fieldLabel
12479                             },
12480                             indicator
12481                         ]
12482                     },
12483                     {
12484                         cls : "", 
12485                         cn: [
12486                             combobox
12487                         ]
12488                     }
12489
12490                 ];
12491                 
12492                 labelCfg = cfg.cn[0];
12493                 contentCfg = cfg.cn[1];
12494             }
12495             
12496             if(this.labelWidth > 12){
12497                 labelCfg.style = "width: " + this.labelWidth + 'px';
12498             }
12499             
12500             if(this.labelWidth < 13 && this.labelmd == 0){
12501                 this.labelmd = this.labelWidth;
12502             }
12503             
12504             if(this.labellg > 0){
12505                 labelCfg.cls += ' col-lg-' + this.labellg;
12506                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12507             }
12508             
12509             if(this.labelmd > 0){
12510                 labelCfg.cls += ' col-md-' + this.labelmd;
12511                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12512             }
12513             
12514             if(this.labelsm > 0){
12515                 labelCfg.cls += ' col-sm-' + this.labelsm;
12516                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12517             }
12518             
12519             if(this.labelxs > 0){
12520                 labelCfg.cls += ' col-xs-' + this.labelxs;
12521                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12522             }
12523             
12524         } else if ( this.fieldLabel.length) {
12525 //                Roo.log(" label");
12526             cfg.cn = [
12527                 indicator,
12528                {
12529                    tag: 'label',
12530                    //cls : 'input-group-addon',
12531                    html : this.fieldLabel
12532
12533                },
12534
12535                combobox
12536
12537             ];
12538             
12539             if(this.indicatorpos == 'right'){
12540                 
12541                 cfg.cn = [
12542                     {
12543                        tag: 'label',
12544                        cn : [
12545                            {
12546                                tag : 'span',
12547                                html : this.fieldLabel
12548                            },
12549                            indicator
12550                        ]
12551
12552                     },
12553                     combobox
12554
12555                 ];
12556
12557             }
12558
12559         } else {
12560             
12561 //                Roo.log(" no label && no align");
12562                 cfg = combobox
12563                      
12564                 
12565         }
12566         
12567         var settings=this;
12568         ['xs','sm','md','lg'].map(function(size){
12569             if (settings[size]) {
12570                 cfg.cls += ' col-' + size + '-' + settings[size];
12571             }
12572         });
12573         
12574         return cfg;
12575         
12576     },
12577     
12578     
12579     
12580     // private
12581     onResize : function(w, h){
12582 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12583 //        if(typeof w == 'number'){
12584 //            var x = w - this.trigger.getWidth();
12585 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12586 //            this.trigger.setStyle('left', x+'px');
12587 //        }
12588     },
12589
12590     // private
12591     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12592
12593     // private
12594     getResizeEl : function(){
12595         return this.inputEl();
12596     },
12597
12598     // private
12599     getPositionEl : function(){
12600         return this.inputEl();
12601     },
12602
12603     // private
12604     alignErrorIcon : function(){
12605         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12606     },
12607
12608     // private
12609     initEvents : function(){
12610         
12611         this.createList();
12612         
12613         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12614         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12615         if(!this.multiple && this.showToggleBtn){
12616             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12617             if(this.hideTrigger){
12618                 this.trigger.setDisplayed(false);
12619             }
12620             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12621         }
12622         
12623         if(this.multiple){
12624             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12625         }
12626         
12627         if(this.removable && !this.editable && !this.tickable){
12628             var close = this.closeTriggerEl();
12629             
12630             if(close){
12631                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12632                 close.on('click', this.removeBtnClick, this, close);
12633             }
12634         }
12635         
12636         //this.trigger.addClassOnOver('x-form-trigger-over');
12637         //this.trigger.addClassOnClick('x-form-trigger-click');
12638         
12639         //if(!this.width){
12640         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12641         //}
12642     },
12643     
12644     closeTriggerEl : function()
12645     {
12646         var close = this.el.select('.roo-combo-removable-btn', true).first();
12647         return close ? close : false;
12648     },
12649     
12650     removeBtnClick : function(e, h, el)
12651     {
12652         e.preventDefault();
12653         
12654         if(this.fireEvent("remove", this) !== false){
12655             this.reset();
12656             this.fireEvent("afterremove", this)
12657         }
12658     },
12659     
12660     createList : function()
12661     {
12662         this.list = Roo.get(document.body).createChild({
12663             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12664             cls: 'typeahead typeahead-long dropdown-menu shadow',
12665             style: 'display:none'
12666         });
12667         
12668         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12669         
12670     },
12671
12672     // private
12673     initTrigger : function(){
12674        
12675     },
12676
12677     // private
12678     onDestroy : function(){
12679         if(this.trigger){
12680             this.trigger.removeAllListeners();
12681           //  this.trigger.remove();
12682         }
12683         //if(this.wrap){
12684         //    this.wrap.remove();
12685         //}
12686         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12687     },
12688
12689     // private
12690     onFocus : function(){
12691         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12692         /*
12693         if(!this.mimicing){
12694             this.wrap.addClass('x-trigger-wrap-focus');
12695             this.mimicing = true;
12696             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12697             if(this.monitorTab){
12698                 this.el.on("keydown", this.checkTab, this);
12699             }
12700         }
12701         */
12702     },
12703
12704     // private
12705     checkTab : function(e){
12706         if(e.getKey() == e.TAB){
12707             this.triggerBlur();
12708         }
12709     },
12710
12711     // private
12712     onBlur : function(){
12713         // do nothing
12714     },
12715
12716     // private
12717     mimicBlur : function(e, t){
12718         /*
12719         if(!this.wrap.contains(t) && this.validateBlur()){
12720             this.triggerBlur();
12721         }
12722         */
12723     },
12724
12725     // private
12726     triggerBlur : function(){
12727         this.mimicing = false;
12728         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12729         if(this.monitorTab){
12730             this.el.un("keydown", this.checkTab, this);
12731         }
12732         //this.wrap.removeClass('x-trigger-wrap-focus');
12733         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12734     },
12735
12736     // private
12737     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12738     validateBlur : function(e, t){
12739         return true;
12740     },
12741
12742     // private
12743     onDisable : function(){
12744         this.inputEl().dom.disabled = true;
12745         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12746         //if(this.wrap){
12747         //    this.wrap.addClass('x-item-disabled');
12748         //}
12749     },
12750
12751     // private
12752     onEnable : function(){
12753         this.inputEl().dom.disabled = false;
12754         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12755         //if(this.wrap){
12756         //    this.el.removeClass('x-item-disabled');
12757         //}
12758     },
12759
12760     // private
12761     onShow : function(){
12762         var ae = this.getActionEl();
12763         
12764         if(ae){
12765             ae.dom.style.display = '';
12766             ae.dom.style.visibility = 'visible';
12767         }
12768     },
12769
12770     // private
12771     
12772     onHide : function(){
12773         var ae = this.getActionEl();
12774         ae.dom.style.display = 'none';
12775     },
12776
12777     /**
12778      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12779      * by an implementing function.
12780      * @method
12781      * @param {EventObject} e
12782      */
12783     onTriggerClick : Roo.emptyFn
12784 });
12785  
12786 /*
12787 * Licence: LGPL
12788 */
12789
12790 /**
12791  * @class Roo.bootstrap.CardUploader
12792  * @extends Roo.bootstrap.Button
12793  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12794  * @cfg {Number} errorTimeout default 3000
12795  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12796  * @cfg {Array}  html The button text.
12797
12798  *
12799  * @constructor
12800  * Create a new CardUploader
12801  * @param {Object} config The config object
12802  */
12803
12804 Roo.bootstrap.CardUploader = function(config){
12805     
12806  
12807     
12808     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12809     
12810     
12811     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12812         return r.data.id
12813      });
12814     
12815      this.addEvents({
12816          // raw events
12817         /**
12818          * @event preview
12819          * When a image is clicked on - and needs to display a slideshow or similar..
12820          * @param {Roo.bootstrap.Card} this
12821          * @param {Object} The image information data 
12822          *
12823          */
12824         'preview' : true,
12825          /**
12826          * @event download
12827          * When a the download link is clicked
12828          * @param {Roo.bootstrap.Card} this
12829          * @param {Object} The image information data  contains 
12830          */
12831         'download' : true
12832         
12833     });
12834 };
12835  
12836 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12837     
12838      
12839     errorTimeout : 3000,
12840      
12841     images : false,
12842    
12843     fileCollection : false,
12844     allowBlank : true,
12845     
12846     getAutoCreate : function()
12847     {
12848         
12849         var cfg =  {
12850             cls :'form-group' ,
12851             cn : [
12852                
12853                 {
12854                     tag: 'label',
12855                    //cls : 'input-group-addon',
12856                     html : this.fieldLabel
12857
12858                 },
12859
12860                 {
12861                     tag: 'input',
12862                     type : 'hidden',
12863                     name : this.name,
12864                     value : this.value,
12865                     cls : 'd-none  form-control'
12866                 },
12867                 
12868                 {
12869                     tag: 'input',
12870                     multiple : 'multiple',
12871                     type : 'file',
12872                     cls : 'd-none  roo-card-upload-selector'
12873                 },
12874                 
12875                 {
12876                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12877                 },
12878                 {
12879                     cls : 'card-columns roo-card-uploader-container'
12880                 }
12881
12882             ]
12883         };
12884            
12885          
12886         return cfg;
12887     },
12888     
12889     getChildContainer : function() /// what children are added to.
12890     {
12891         return this.containerEl;
12892     },
12893    
12894     getButtonContainer : function() /// what children are added to.
12895     {
12896         return this.el.select(".roo-card-uploader-button-container").first();
12897     },
12898    
12899     initEvents : function()
12900     {
12901         
12902         Roo.bootstrap.Input.prototype.initEvents.call(this);
12903         
12904         var t = this;
12905         this.addxtype({
12906             xns: Roo.bootstrap,
12907
12908             xtype : 'Button',
12909             container_method : 'getButtonContainer' ,            
12910             html :  this.html, // fix changable?
12911             cls : 'w-100 ',
12912             listeners : {
12913                 'click' : function(btn, e) {
12914                     t.onClick(e);
12915                 }
12916             }
12917         });
12918         
12919         
12920         
12921         
12922         this.urlAPI = (window.createObjectURL && window) || 
12923                                 (window.URL && URL.revokeObjectURL && URL) || 
12924                                 (window.webkitURL && webkitURL);
12925                         
12926          
12927          
12928          
12929         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12930         
12931         this.selectorEl.on('change', this.onFileSelected, this);
12932         if (this.images) {
12933             var t = this;
12934             this.images.forEach(function(img) {
12935                 t.addCard(img)
12936             });
12937             this.images = false;
12938         }
12939         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12940          
12941        
12942     },
12943     
12944    
12945     onClick : function(e)
12946     {
12947         e.preventDefault();
12948          
12949         this.selectorEl.dom.click();
12950          
12951     },
12952     
12953     onFileSelected : function(e)
12954     {
12955         e.preventDefault();
12956         
12957         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12958             return;
12959         }
12960         
12961         Roo.each(this.selectorEl.dom.files, function(file){    
12962             this.addFile(file);
12963         }, this);
12964          
12965     },
12966     
12967       
12968     
12969       
12970     
12971     addFile : function(file)
12972     {
12973            
12974         if(typeof(file) === 'string'){
12975             throw "Add file by name?"; // should not happen
12976             return;
12977         }
12978         
12979         if(!file || !this.urlAPI){
12980             return;
12981         }
12982         
12983         // file;
12984         // file.type;
12985         
12986         var _this = this;
12987         
12988         
12989         var url = _this.urlAPI.createObjectURL( file);
12990            
12991         this.addCard({
12992             id : Roo.bootstrap.CardUploader.ID--,
12993             is_uploaded : false,
12994             src : url,
12995             srcfile : file,
12996             title : file.name,
12997             mimetype : file.type,
12998             preview : false,
12999             is_deleted : 0
13000         });
13001         
13002     },
13003     
13004     /**
13005      * addCard - add an Attachment to the uploader
13006      * @param data - the data about the image to upload
13007      *
13008      * {
13009           id : 123
13010           title : "Title of file",
13011           is_uploaded : false,
13012           src : "http://.....",
13013           srcfile : { the File upload object },
13014           mimetype : file.type,
13015           preview : false,
13016           is_deleted : 0
13017           .. any other data...
13018         }
13019      *
13020      * 
13021     */
13022     
13023     addCard : function (data)
13024     {
13025         // hidden input element?
13026         // if the file is not an image...
13027         //then we need to use something other that and header_image
13028         var t = this;
13029         //   remove.....
13030         var footer = [
13031             {
13032                 xns : Roo.bootstrap,
13033                 xtype : 'CardFooter',
13034                  items: [
13035                     {
13036                         xns : Roo.bootstrap,
13037                         xtype : 'Element',
13038                         cls : 'd-flex',
13039                         items : [
13040                             
13041                             {
13042                                 xns : Roo.bootstrap,
13043                                 xtype : 'Button',
13044                                 html : String.format("<small>{0}</small>", data.title),
13045                                 cls : 'col-10 text-left',
13046                                 size: 'sm',
13047                                 weight: 'link',
13048                                 fa : 'download',
13049                                 listeners : {
13050                                     click : function() {
13051                                      
13052                                         t.fireEvent( "download", t, data );
13053                                     }
13054                                 }
13055                             },
13056                           
13057                             {
13058                                 xns : Roo.bootstrap,
13059                                 xtype : 'Button',
13060                                 style: 'max-height: 28px; ',
13061                                 size : 'sm',
13062                                 weight: 'danger',
13063                                 cls : 'col-2',
13064                                 fa : 'times',
13065                                 listeners : {
13066                                     click : function() {
13067                                         t.removeCard(data.id)
13068                                     }
13069                                 }
13070                             }
13071                         ]
13072                     }
13073                     
13074                 ] 
13075             }
13076             
13077         ];
13078         
13079         var cn = this.addxtype(
13080             {
13081                  
13082                 xns : Roo.bootstrap,
13083                 xtype : 'Card',
13084                 closeable : true,
13085                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13086                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13087                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13088                 data : data,
13089                 html : false,
13090                  
13091                 items : footer,
13092                 initEvents : function() {
13093                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13094                     var card = this;
13095                     this.imgEl = this.el.select('.card-img-top').first();
13096                     if (this.imgEl) {
13097                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13098                         this.imgEl.set({ 'pointer' : 'cursor' });
13099                                   
13100                     }
13101                     this.getCardFooter().addClass('p-1');
13102                     
13103                   
13104                 }
13105                 
13106             }
13107         );
13108         // dont' really need ot update items.
13109         // this.items.push(cn);
13110         this.fileCollection.add(cn);
13111         
13112         if (!data.srcfile) {
13113             this.updateInput();
13114             return;
13115         }
13116             
13117         var _t = this;
13118         var reader = new FileReader();
13119         reader.addEventListener("load", function() {  
13120             data.srcdata =  reader.result;
13121             _t.updateInput();
13122         });
13123         reader.readAsDataURL(data.srcfile);
13124         
13125         
13126         
13127     },
13128     removeCard : function(id)
13129     {
13130         
13131         var card  = this.fileCollection.get(id);
13132         card.data.is_deleted = 1;
13133         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13134         //this.fileCollection.remove(card);
13135         //this.items = this.items.filter(function(e) { return e != card });
13136         // dont' really need ot update items.
13137         card.el.dom.parentNode.removeChild(card.el.dom);
13138         this.updateInput();
13139
13140         
13141     },
13142     reset: function()
13143     {
13144         this.fileCollection.each(function(card) {
13145             if (card.el.dom && card.el.dom.parentNode) {
13146                 card.el.dom.parentNode.removeChild(card.el.dom);
13147             }
13148         });
13149         this.fileCollection.clear();
13150         this.updateInput();
13151     },
13152     
13153     updateInput : function()
13154     {
13155          var data = [];
13156         this.fileCollection.each(function(e) {
13157             data.push(e.data);
13158             
13159         });
13160         this.inputEl().dom.value = JSON.stringify(data);
13161         
13162         
13163         
13164     }
13165     
13166     
13167 });
13168
13169
13170 Roo.bootstrap.CardUploader.ID = -1;/*
13171  * Based on:
13172  * Ext JS Library 1.1.1
13173  * Copyright(c) 2006-2007, Ext JS, LLC.
13174  *
13175  * Originally Released Under LGPL - original licence link has changed is not relivant.
13176  *
13177  * Fork - LGPL
13178  * <script type="text/javascript">
13179  */
13180
13181
13182 /**
13183  * @class Roo.data.SortTypes
13184  * @singleton
13185  * Defines the default sorting (casting?) comparison functions used when sorting data.
13186  */
13187 Roo.data.SortTypes = {
13188     /**
13189      * Default sort that does nothing
13190      * @param {Mixed} s The value being converted
13191      * @return {Mixed} The comparison value
13192      */
13193     none : function(s){
13194         return s;
13195     },
13196     
13197     /**
13198      * The regular expression used to strip tags
13199      * @type {RegExp}
13200      * @property
13201      */
13202     stripTagsRE : /<\/?[^>]+>/gi,
13203     
13204     /**
13205      * Strips all HTML tags to sort on text only
13206      * @param {Mixed} s The value being converted
13207      * @return {String} The comparison value
13208      */
13209     asText : function(s){
13210         return String(s).replace(this.stripTagsRE, "");
13211     },
13212     
13213     /**
13214      * Strips all HTML tags to sort on text only - Case insensitive
13215      * @param {Mixed} s The value being converted
13216      * @return {String} The comparison value
13217      */
13218     asUCText : function(s){
13219         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13220     },
13221     
13222     /**
13223      * Case insensitive string
13224      * @param {Mixed} s The value being converted
13225      * @return {String} The comparison value
13226      */
13227     asUCString : function(s) {
13228         return String(s).toUpperCase();
13229     },
13230     
13231     /**
13232      * Date sorting
13233      * @param {Mixed} s The value being converted
13234      * @return {Number} The comparison value
13235      */
13236     asDate : function(s) {
13237         if(!s){
13238             return 0;
13239         }
13240         if(s instanceof Date){
13241             return s.getTime();
13242         }
13243         return Date.parse(String(s));
13244     },
13245     
13246     /**
13247      * Float sorting
13248      * @param {Mixed} s The value being converted
13249      * @return {Float} The comparison value
13250      */
13251     asFloat : function(s) {
13252         var val = parseFloat(String(s).replace(/,/g, ""));
13253         if(isNaN(val)) {
13254             val = 0;
13255         }
13256         return val;
13257     },
13258     
13259     /**
13260      * Integer sorting
13261      * @param {Mixed} s The value being converted
13262      * @return {Number} The comparison value
13263      */
13264     asInt : function(s) {
13265         var val = parseInt(String(s).replace(/,/g, ""));
13266         if(isNaN(val)) {
13267             val = 0;
13268         }
13269         return val;
13270     }
13271 };/*
13272  * Based on:
13273  * Ext JS Library 1.1.1
13274  * Copyright(c) 2006-2007, Ext JS, LLC.
13275  *
13276  * Originally Released Under LGPL - original licence link has changed is not relivant.
13277  *
13278  * Fork - LGPL
13279  * <script type="text/javascript">
13280  */
13281
13282 /**
13283 * @class Roo.data.Record
13284  * Instances of this class encapsulate both record <em>definition</em> information, and record
13285  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13286  * to access Records cached in an {@link Roo.data.Store} object.<br>
13287  * <p>
13288  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13289  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13290  * objects.<br>
13291  * <p>
13292  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13293  * @constructor
13294  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13295  * {@link #create}. The parameters are the same.
13296  * @param {Array} data An associative Array of data values keyed by the field name.
13297  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13298  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13299  * not specified an integer id is generated.
13300  */
13301 Roo.data.Record = function(data, id){
13302     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13303     this.data = data;
13304 };
13305
13306 /**
13307  * Generate a constructor for a specific record layout.
13308  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13309  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13310  * Each field definition object may contain the following properties: <ul>
13311  * <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,
13312  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13313  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13314  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13315  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13316  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13317  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13318  * this may be omitted.</p></li>
13319  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13320  * <ul><li>auto (Default, implies no conversion)</li>
13321  * <li>string</li>
13322  * <li>int</li>
13323  * <li>float</li>
13324  * <li>boolean</li>
13325  * <li>date</li></ul></p></li>
13326  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13327  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13328  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13329  * by the Reader into an object that will be stored in the Record. It is passed the
13330  * following parameters:<ul>
13331  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13332  * </ul></p></li>
13333  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13334  * </ul>
13335  * <br>usage:<br><pre><code>
13336 var TopicRecord = Roo.data.Record.create(
13337     {name: 'title', mapping: 'topic_title'},
13338     {name: 'author', mapping: 'username'},
13339     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13340     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13341     {name: 'lastPoster', mapping: 'user2'},
13342     {name: 'excerpt', mapping: 'post_text'}
13343 );
13344
13345 var myNewRecord = new TopicRecord({
13346     title: 'Do my job please',
13347     author: 'noobie',
13348     totalPosts: 1,
13349     lastPost: new Date(),
13350     lastPoster: 'Animal',
13351     excerpt: 'No way dude!'
13352 });
13353 myStore.add(myNewRecord);
13354 </code></pre>
13355  * @method create
13356  * @static
13357  */
13358 Roo.data.Record.create = function(o){
13359     var f = function(){
13360         f.superclass.constructor.apply(this, arguments);
13361     };
13362     Roo.extend(f, Roo.data.Record);
13363     var p = f.prototype;
13364     p.fields = new Roo.util.MixedCollection(false, function(field){
13365         return field.name;
13366     });
13367     for(var i = 0, len = o.length; i < len; i++){
13368         p.fields.add(new Roo.data.Field(o[i]));
13369     }
13370     f.getField = function(name){
13371         return p.fields.get(name);  
13372     };
13373     return f;
13374 };
13375
13376 Roo.data.Record.AUTO_ID = 1000;
13377 Roo.data.Record.EDIT = 'edit';
13378 Roo.data.Record.REJECT = 'reject';
13379 Roo.data.Record.COMMIT = 'commit';
13380
13381 Roo.data.Record.prototype = {
13382     /**
13383      * Readonly flag - true if this record has been modified.
13384      * @type Boolean
13385      */
13386     dirty : false,
13387     editing : false,
13388     error: null,
13389     modified: null,
13390
13391     // private
13392     join : function(store){
13393         this.store = store;
13394     },
13395
13396     /**
13397      * Set the named field to the specified value.
13398      * @param {String} name The name of the field to set.
13399      * @param {Object} value The value to set the field to.
13400      */
13401     set : function(name, value){
13402         if(this.data[name] == value){
13403             return;
13404         }
13405         this.dirty = true;
13406         if(!this.modified){
13407             this.modified = {};
13408         }
13409         if(typeof this.modified[name] == 'undefined'){
13410             this.modified[name] = this.data[name];
13411         }
13412         this.data[name] = value;
13413         if(!this.editing && this.store){
13414             this.store.afterEdit(this);
13415         }       
13416     },
13417
13418     /**
13419      * Get the value of the named field.
13420      * @param {String} name The name of the field to get the value of.
13421      * @return {Object} The value of the field.
13422      */
13423     get : function(name){
13424         return this.data[name]; 
13425     },
13426
13427     // private
13428     beginEdit : function(){
13429         this.editing = true;
13430         this.modified = {}; 
13431     },
13432
13433     // private
13434     cancelEdit : function(){
13435         this.editing = false;
13436         delete this.modified;
13437     },
13438
13439     // private
13440     endEdit : function(){
13441         this.editing = false;
13442         if(this.dirty && this.store){
13443             this.store.afterEdit(this);
13444         }
13445     },
13446
13447     /**
13448      * Usually called by the {@link Roo.data.Store} which owns the Record.
13449      * Rejects all changes made to the Record since either creation, or the last commit operation.
13450      * Modified fields are reverted to their original values.
13451      * <p>
13452      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13453      * of reject operations.
13454      */
13455     reject : function(){
13456         var m = this.modified;
13457         for(var n in m){
13458             if(typeof m[n] != "function"){
13459                 this.data[n] = m[n];
13460             }
13461         }
13462         this.dirty = false;
13463         delete this.modified;
13464         this.editing = false;
13465         if(this.store){
13466             this.store.afterReject(this);
13467         }
13468     },
13469
13470     /**
13471      * Usually called by the {@link Roo.data.Store} which owns the Record.
13472      * Commits all changes made to the Record since either creation, or the last commit operation.
13473      * <p>
13474      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13475      * of commit operations.
13476      */
13477     commit : function(){
13478         this.dirty = false;
13479         delete this.modified;
13480         this.editing = false;
13481         if(this.store){
13482             this.store.afterCommit(this);
13483         }
13484     },
13485
13486     // private
13487     hasError : function(){
13488         return this.error != null;
13489     },
13490
13491     // private
13492     clearError : function(){
13493         this.error = null;
13494     },
13495
13496     /**
13497      * Creates a copy of this record.
13498      * @param {String} id (optional) A new record id if you don't want to use this record's id
13499      * @return {Record}
13500      */
13501     copy : function(newId) {
13502         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13503     }
13504 };/*
13505  * Based on:
13506  * Ext JS Library 1.1.1
13507  * Copyright(c) 2006-2007, Ext JS, LLC.
13508  *
13509  * Originally Released Under LGPL - original licence link has changed is not relivant.
13510  *
13511  * Fork - LGPL
13512  * <script type="text/javascript">
13513  */
13514
13515
13516
13517 /**
13518  * @class Roo.data.Store
13519  * @extends Roo.util.Observable
13520  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13521  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13522  * <p>
13523  * 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
13524  * has no knowledge of the format of the data returned by the Proxy.<br>
13525  * <p>
13526  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13527  * instances from the data object. These records are cached and made available through accessor functions.
13528  * @constructor
13529  * Creates a new Store.
13530  * @param {Object} config A config object containing the objects needed for the Store to access data,
13531  * and read the data into Records.
13532  */
13533 Roo.data.Store = function(config){
13534     this.data = new Roo.util.MixedCollection(false);
13535     this.data.getKey = function(o){
13536         return o.id;
13537     };
13538     this.baseParams = {};
13539     // private
13540     this.paramNames = {
13541         "start" : "start",
13542         "limit" : "limit",
13543         "sort" : "sort",
13544         "dir" : "dir",
13545         "multisort" : "_multisort"
13546     };
13547
13548     if(config && config.data){
13549         this.inlineData = config.data;
13550         delete config.data;
13551     }
13552
13553     Roo.apply(this, config);
13554     
13555     if(this.reader){ // reader passed
13556         this.reader = Roo.factory(this.reader, Roo.data);
13557         this.reader.xmodule = this.xmodule || false;
13558         if(!this.recordType){
13559             this.recordType = this.reader.recordType;
13560         }
13561         if(this.reader.onMetaChange){
13562             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13563         }
13564     }
13565
13566     if(this.recordType){
13567         this.fields = this.recordType.prototype.fields;
13568     }
13569     this.modified = [];
13570
13571     this.addEvents({
13572         /**
13573          * @event datachanged
13574          * Fires when the data cache has changed, and a widget which is using this Store
13575          * as a Record cache should refresh its view.
13576          * @param {Store} this
13577          */
13578         datachanged : true,
13579         /**
13580          * @event metachange
13581          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13582          * @param {Store} this
13583          * @param {Object} meta The JSON metadata
13584          */
13585         metachange : true,
13586         /**
13587          * @event add
13588          * Fires when Records have been added to the Store
13589          * @param {Store} this
13590          * @param {Roo.data.Record[]} records The array of Records added
13591          * @param {Number} index The index at which the record(s) were added
13592          */
13593         add : true,
13594         /**
13595          * @event remove
13596          * Fires when a Record has been removed from the Store
13597          * @param {Store} this
13598          * @param {Roo.data.Record} record The Record that was removed
13599          * @param {Number} index The index at which the record was removed
13600          */
13601         remove : true,
13602         /**
13603          * @event update
13604          * Fires when a Record has been updated
13605          * @param {Store} this
13606          * @param {Roo.data.Record} record The Record that was updated
13607          * @param {String} operation The update operation being performed.  Value may be one of:
13608          * <pre><code>
13609  Roo.data.Record.EDIT
13610  Roo.data.Record.REJECT
13611  Roo.data.Record.COMMIT
13612          * </code></pre>
13613          */
13614         update : true,
13615         /**
13616          * @event clear
13617          * Fires when the data cache has been cleared.
13618          * @param {Store} this
13619          */
13620         clear : true,
13621         /**
13622          * @event beforeload
13623          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13624          * the load action will be canceled.
13625          * @param {Store} this
13626          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13627          */
13628         beforeload : true,
13629         /**
13630          * @event beforeloadadd
13631          * Fires after a new set of Records has been loaded.
13632          * @param {Store} this
13633          * @param {Roo.data.Record[]} records The Records that were loaded
13634          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13635          */
13636         beforeloadadd : true,
13637         /**
13638          * @event load
13639          * Fires after a new set of Records has been loaded, before they are added to the store.
13640          * @param {Store} this
13641          * @param {Roo.data.Record[]} records The Records that were loaded
13642          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13643          * @params {Object} return from reader
13644          */
13645         load : true,
13646         /**
13647          * @event loadexception
13648          * Fires if an exception occurs in the Proxy during loading.
13649          * Called with the signature of the Proxy's "loadexception" event.
13650          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13651          * 
13652          * @param {Proxy} 
13653          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13654          * @param {Object} load options 
13655          * @param {Object} jsonData from your request (normally this contains the Exception)
13656          */
13657         loadexception : true
13658     });
13659     
13660     if(this.proxy){
13661         this.proxy = Roo.factory(this.proxy, Roo.data);
13662         this.proxy.xmodule = this.xmodule || false;
13663         this.relayEvents(this.proxy,  ["loadexception"]);
13664     }
13665     this.sortToggle = {};
13666     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13667
13668     Roo.data.Store.superclass.constructor.call(this);
13669
13670     if(this.inlineData){
13671         this.loadData(this.inlineData);
13672         delete this.inlineData;
13673     }
13674 };
13675
13676 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13677      /**
13678     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13679     * without a remote query - used by combo/forms at present.
13680     */
13681     
13682     /**
13683     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13684     */
13685     /**
13686     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13687     */
13688     /**
13689     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13690     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13691     */
13692     /**
13693     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13694     * on any HTTP request
13695     */
13696     /**
13697     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13698     */
13699     /**
13700     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13701     */
13702     multiSort: false,
13703     /**
13704     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13705     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13706     */
13707     remoteSort : false,
13708
13709     /**
13710     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13711      * loaded or when a record is removed. (defaults to false).
13712     */
13713     pruneModifiedRecords : false,
13714
13715     // private
13716     lastOptions : null,
13717
13718     /**
13719      * Add Records to the Store and fires the add event.
13720      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13721      */
13722     add : function(records){
13723         records = [].concat(records);
13724         for(var i = 0, len = records.length; i < len; i++){
13725             records[i].join(this);
13726         }
13727         var index = this.data.length;
13728         this.data.addAll(records);
13729         this.fireEvent("add", this, records, index);
13730     },
13731
13732     /**
13733      * Remove a Record from the Store and fires the remove event.
13734      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13735      */
13736     remove : function(record){
13737         var index = this.data.indexOf(record);
13738         this.data.removeAt(index);
13739  
13740         if(this.pruneModifiedRecords){
13741             this.modified.remove(record);
13742         }
13743         this.fireEvent("remove", this, record, index);
13744     },
13745
13746     /**
13747      * Remove all Records from the Store and fires the clear event.
13748      */
13749     removeAll : function(){
13750         this.data.clear();
13751         if(this.pruneModifiedRecords){
13752             this.modified = [];
13753         }
13754         this.fireEvent("clear", this);
13755     },
13756
13757     /**
13758      * Inserts Records to the Store at the given index and fires the add event.
13759      * @param {Number} index The start index at which to insert the passed Records.
13760      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13761      */
13762     insert : function(index, records){
13763         records = [].concat(records);
13764         for(var i = 0, len = records.length; i < len; i++){
13765             this.data.insert(index, records[i]);
13766             records[i].join(this);
13767         }
13768         this.fireEvent("add", this, records, index);
13769     },
13770
13771     /**
13772      * Get the index within the cache of the passed Record.
13773      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13774      * @return {Number} The index of the passed Record. Returns -1 if not found.
13775      */
13776     indexOf : function(record){
13777         return this.data.indexOf(record);
13778     },
13779
13780     /**
13781      * Get the index within the cache of the Record with the passed id.
13782      * @param {String} id The id of the Record to find.
13783      * @return {Number} The index of the Record. Returns -1 if not found.
13784      */
13785     indexOfId : function(id){
13786         return this.data.indexOfKey(id);
13787     },
13788
13789     /**
13790      * Get the Record with the specified id.
13791      * @param {String} id The id of the Record to find.
13792      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13793      */
13794     getById : function(id){
13795         return this.data.key(id);
13796     },
13797
13798     /**
13799      * Get the Record at the specified index.
13800      * @param {Number} index The index of the Record to find.
13801      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13802      */
13803     getAt : function(index){
13804         return this.data.itemAt(index);
13805     },
13806
13807     /**
13808      * Returns a range of Records between specified indices.
13809      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13810      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13811      * @return {Roo.data.Record[]} An array of Records
13812      */
13813     getRange : function(start, end){
13814         return this.data.getRange(start, end);
13815     },
13816
13817     // private
13818     storeOptions : function(o){
13819         o = Roo.apply({}, o);
13820         delete o.callback;
13821         delete o.scope;
13822         this.lastOptions = o;
13823     },
13824
13825     /**
13826      * Loads the Record cache from the configured Proxy using the configured Reader.
13827      * <p>
13828      * If using remote paging, then the first load call must specify the <em>start</em>
13829      * and <em>limit</em> properties in the options.params property to establish the initial
13830      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13831      * <p>
13832      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13833      * and this call will return before the new data has been loaded. Perform any post-processing
13834      * in a callback function, or in a "load" event handler.</strong>
13835      * <p>
13836      * @param {Object} options An object containing properties which control loading options:<ul>
13837      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13838      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13839      * passed the following arguments:<ul>
13840      * <li>r : Roo.data.Record[]</li>
13841      * <li>options: Options object from the load call</li>
13842      * <li>success: Boolean success indicator</li></ul></li>
13843      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13844      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13845      * </ul>
13846      */
13847     load : function(options){
13848         options = options || {};
13849         if(this.fireEvent("beforeload", this, options) !== false){
13850             this.storeOptions(options);
13851             var p = Roo.apply(options.params || {}, this.baseParams);
13852             // if meta was not loaded from remote source.. try requesting it.
13853             if (!this.reader.metaFromRemote) {
13854                 p._requestMeta = 1;
13855             }
13856             if(this.sortInfo && this.remoteSort){
13857                 var pn = this.paramNames;
13858                 p[pn["sort"]] = this.sortInfo.field;
13859                 p[pn["dir"]] = this.sortInfo.direction;
13860             }
13861             if (this.multiSort) {
13862                 var pn = this.paramNames;
13863                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13864             }
13865             
13866             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13867         }
13868     },
13869
13870     /**
13871      * Reloads the Record cache from the configured Proxy using the configured Reader and
13872      * the options from the last load operation performed.
13873      * @param {Object} options (optional) An object containing properties which may override the options
13874      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13875      * the most recently used options are reused).
13876      */
13877     reload : function(options){
13878         this.load(Roo.applyIf(options||{}, this.lastOptions));
13879     },
13880
13881     // private
13882     // Called as a callback by the Reader during a load operation.
13883     loadRecords : function(o, options, success){
13884         if(!o || success === false){
13885             if(success !== false){
13886                 this.fireEvent("load", this, [], options, o);
13887             }
13888             if(options.callback){
13889                 options.callback.call(options.scope || this, [], options, false);
13890             }
13891             return;
13892         }
13893         // if data returned failure - throw an exception.
13894         if (o.success === false) {
13895             // show a message if no listener is registered.
13896             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13897                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13898             }
13899             // loadmask wil be hooked into this..
13900             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13901             return;
13902         }
13903         var r = o.records, t = o.totalRecords || r.length;
13904         
13905         this.fireEvent("beforeloadadd", this, r, options, o);
13906         
13907         if(!options || options.add !== true){
13908             if(this.pruneModifiedRecords){
13909                 this.modified = [];
13910             }
13911             for(var i = 0, len = r.length; i < len; i++){
13912                 r[i].join(this);
13913             }
13914             if(this.snapshot){
13915                 this.data = this.snapshot;
13916                 delete this.snapshot;
13917             }
13918             this.data.clear();
13919             this.data.addAll(r);
13920             this.totalLength = t;
13921             this.applySort();
13922             this.fireEvent("datachanged", this);
13923         }else{
13924             this.totalLength = Math.max(t, this.data.length+r.length);
13925             this.add(r);
13926         }
13927         
13928         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13929                 
13930             var e = new Roo.data.Record({});
13931
13932             e.set(this.parent.displayField, this.parent.emptyTitle);
13933             e.set(this.parent.valueField, '');
13934
13935             this.insert(0, e);
13936         }
13937             
13938         this.fireEvent("load", this, r, options, o);
13939         if(options.callback){
13940             options.callback.call(options.scope || this, r, options, true);
13941         }
13942     },
13943
13944
13945     /**
13946      * Loads data from a passed data block. A Reader which understands the format of the data
13947      * must have been configured in the constructor.
13948      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13949      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13950      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13951      */
13952     loadData : function(o, append){
13953         var r = this.reader.readRecords(o);
13954         this.loadRecords(r, {add: append}, true);
13955     },
13956     
13957      /**
13958      * using 'cn' the nested child reader read the child array into it's child stores.
13959      * @param {Object} rec The record with a 'children array
13960      */
13961     loadDataFromChildren : function(rec)
13962     {
13963         this.loadData(this.reader.toLoadData(rec));
13964     },
13965     
13966
13967     /**
13968      * Gets the number of cached records.
13969      * <p>
13970      * <em>If using paging, this may not be the total size of the dataset. If the data object
13971      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13972      * the data set size</em>
13973      */
13974     getCount : function(){
13975         return this.data.length || 0;
13976     },
13977
13978     /**
13979      * Gets the total number of records in the dataset as returned by the server.
13980      * <p>
13981      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13982      * the dataset size</em>
13983      */
13984     getTotalCount : function(){
13985         return this.totalLength || 0;
13986     },
13987
13988     /**
13989      * Returns the sort state of the Store as an object with two properties:
13990      * <pre><code>
13991  field {String} The name of the field by which the Records are sorted
13992  direction {String} The sort order, "ASC" or "DESC"
13993      * </code></pre>
13994      */
13995     getSortState : function(){
13996         return this.sortInfo;
13997     },
13998
13999     // private
14000     applySort : function(){
14001         if(this.sortInfo && !this.remoteSort){
14002             var s = this.sortInfo, f = s.field;
14003             var st = this.fields.get(f).sortType;
14004             var fn = function(r1, r2){
14005                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14006                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14007             };
14008             this.data.sort(s.direction, fn);
14009             if(this.snapshot && this.snapshot != this.data){
14010                 this.snapshot.sort(s.direction, fn);
14011             }
14012         }
14013     },
14014
14015     /**
14016      * Sets the default sort column and order to be used by the next load operation.
14017      * @param {String} fieldName The name of the field to sort by.
14018      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14019      */
14020     setDefaultSort : function(field, dir){
14021         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14022     },
14023
14024     /**
14025      * Sort the Records.
14026      * If remote sorting is used, the sort is performed on the server, and the cache is
14027      * reloaded. If local sorting is used, the cache is sorted internally.
14028      * @param {String} fieldName The name of the field to sort by.
14029      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14030      */
14031     sort : function(fieldName, dir){
14032         var f = this.fields.get(fieldName);
14033         if(!dir){
14034             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14035             
14036             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14037                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14038             }else{
14039                 dir = f.sortDir;
14040             }
14041         }
14042         this.sortToggle[f.name] = dir;
14043         this.sortInfo = {field: f.name, direction: dir};
14044         if(!this.remoteSort){
14045             this.applySort();
14046             this.fireEvent("datachanged", this);
14047         }else{
14048             this.load(this.lastOptions);
14049         }
14050     },
14051
14052     /**
14053      * Calls the specified function for each of the Records in the cache.
14054      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14055      * Returning <em>false</em> aborts and exits the iteration.
14056      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14057      */
14058     each : function(fn, scope){
14059         this.data.each(fn, scope);
14060     },
14061
14062     /**
14063      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14064      * (e.g., during paging).
14065      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14066      */
14067     getModifiedRecords : function(){
14068         return this.modified;
14069     },
14070
14071     // private
14072     createFilterFn : function(property, value, anyMatch){
14073         if(!value.exec){ // not a regex
14074             value = String(value);
14075             if(value.length == 0){
14076                 return false;
14077             }
14078             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14079         }
14080         return function(r){
14081             return value.test(r.data[property]);
14082         };
14083     },
14084
14085     /**
14086      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14087      * @param {String} property A field on your records
14088      * @param {Number} start The record index to start at (defaults to 0)
14089      * @param {Number} end The last record index to include (defaults to length - 1)
14090      * @return {Number} The sum
14091      */
14092     sum : function(property, start, end){
14093         var rs = this.data.items, v = 0;
14094         start = start || 0;
14095         end = (end || end === 0) ? end : rs.length-1;
14096
14097         for(var i = start; i <= end; i++){
14098             v += (rs[i].data[property] || 0);
14099         }
14100         return v;
14101     },
14102
14103     /**
14104      * Filter the records by a specified property.
14105      * @param {String} field A field on your records
14106      * @param {String/RegExp} value Either a string that the field
14107      * should start with or a RegExp to test against the field
14108      * @param {Boolean} anyMatch True to match any part not just the beginning
14109      */
14110     filter : function(property, value, anyMatch){
14111         var fn = this.createFilterFn(property, value, anyMatch);
14112         return fn ? this.filterBy(fn) : this.clearFilter();
14113     },
14114
14115     /**
14116      * Filter by a function. The specified function will be called with each
14117      * record in this data source. If the function returns true the record is included,
14118      * otherwise it is filtered.
14119      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14120      * @param {Object} scope (optional) The scope of the function (defaults to this)
14121      */
14122     filterBy : function(fn, scope){
14123         this.snapshot = this.snapshot || this.data;
14124         this.data = this.queryBy(fn, scope||this);
14125         this.fireEvent("datachanged", this);
14126     },
14127
14128     /**
14129      * Query the records by a specified property.
14130      * @param {String} field A field on your records
14131      * @param {String/RegExp} value Either a string that the field
14132      * should start with or a RegExp to test against the field
14133      * @param {Boolean} anyMatch True to match any part not just the beginning
14134      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14135      */
14136     query : function(property, value, anyMatch){
14137         var fn = this.createFilterFn(property, value, anyMatch);
14138         return fn ? this.queryBy(fn) : this.data.clone();
14139     },
14140
14141     /**
14142      * Query by a function. The specified function will be called with each
14143      * record in this data source. If the function returns true the record is included
14144      * in the results.
14145      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14146      * @param {Object} scope (optional) The scope of the function (defaults to this)
14147       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14148      **/
14149     queryBy : function(fn, scope){
14150         var data = this.snapshot || this.data;
14151         return data.filterBy(fn, scope||this);
14152     },
14153
14154     /**
14155      * Collects unique values for a particular dataIndex from this store.
14156      * @param {String} dataIndex The property to collect
14157      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14158      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14159      * @return {Array} An array of the unique values
14160      **/
14161     collect : function(dataIndex, allowNull, bypassFilter){
14162         var d = (bypassFilter === true && this.snapshot) ?
14163                 this.snapshot.items : this.data.items;
14164         var v, sv, r = [], l = {};
14165         for(var i = 0, len = d.length; i < len; i++){
14166             v = d[i].data[dataIndex];
14167             sv = String(v);
14168             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14169                 l[sv] = true;
14170                 r[r.length] = v;
14171             }
14172         }
14173         return r;
14174     },
14175
14176     /**
14177      * Revert to a view of the Record cache with no filtering applied.
14178      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14179      */
14180     clearFilter : function(suppressEvent){
14181         if(this.snapshot && this.snapshot != this.data){
14182             this.data = this.snapshot;
14183             delete this.snapshot;
14184             if(suppressEvent !== true){
14185                 this.fireEvent("datachanged", this);
14186             }
14187         }
14188     },
14189
14190     // private
14191     afterEdit : function(record){
14192         if(this.modified.indexOf(record) == -1){
14193             this.modified.push(record);
14194         }
14195         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14196     },
14197     
14198     // private
14199     afterReject : function(record){
14200         this.modified.remove(record);
14201         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14202     },
14203
14204     // private
14205     afterCommit : function(record){
14206         this.modified.remove(record);
14207         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14208     },
14209
14210     /**
14211      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14212      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14213      */
14214     commitChanges : function(){
14215         var m = this.modified.slice(0);
14216         this.modified = [];
14217         for(var i = 0, len = m.length; i < len; i++){
14218             m[i].commit();
14219         }
14220     },
14221
14222     /**
14223      * Cancel outstanding changes on all changed records.
14224      */
14225     rejectChanges : function(){
14226         var m = this.modified.slice(0);
14227         this.modified = [];
14228         for(var i = 0, len = m.length; i < len; i++){
14229             m[i].reject();
14230         }
14231     },
14232
14233     onMetaChange : function(meta, rtype, o){
14234         this.recordType = rtype;
14235         this.fields = rtype.prototype.fields;
14236         delete this.snapshot;
14237         this.sortInfo = meta.sortInfo || this.sortInfo;
14238         this.modified = [];
14239         this.fireEvent('metachange', this, this.reader.meta);
14240     },
14241     
14242     moveIndex : function(data, type)
14243     {
14244         var index = this.indexOf(data);
14245         
14246         var newIndex = index + type;
14247         
14248         this.remove(data);
14249         
14250         this.insert(newIndex, data);
14251         
14252     }
14253 });/*
14254  * Based on:
14255  * Ext JS Library 1.1.1
14256  * Copyright(c) 2006-2007, Ext JS, LLC.
14257  *
14258  * Originally Released Under LGPL - original licence link has changed is not relivant.
14259  *
14260  * Fork - LGPL
14261  * <script type="text/javascript">
14262  */
14263
14264 /**
14265  * @class Roo.data.SimpleStore
14266  * @extends Roo.data.Store
14267  * Small helper class to make creating Stores from Array data easier.
14268  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14269  * @cfg {Array} fields An array of field definition objects, or field name strings.
14270  * @cfg {Object} an existing reader (eg. copied from another store)
14271  * @cfg {Array} data The multi-dimensional array of data
14272  * @constructor
14273  * @param {Object} config
14274  */
14275 Roo.data.SimpleStore = function(config)
14276 {
14277     Roo.data.SimpleStore.superclass.constructor.call(this, {
14278         isLocal : true,
14279         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14280                 id: config.id
14281             },
14282             Roo.data.Record.create(config.fields)
14283         ),
14284         proxy : new Roo.data.MemoryProxy(config.data)
14285     });
14286     this.load();
14287 };
14288 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14289  * Based on:
14290  * Ext JS Library 1.1.1
14291  * Copyright(c) 2006-2007, Ext JS, LLC.
14292  *
14293  * Originally Released Under LGPL - original licence link has changed is not relivant.
14294  *
14295  * Fork - LGPL
14296  * <script type="text/javascript">
14297  */
14298
14299 /**
14300 /**
14301  * @extends Roo.data.Store
14302  * @class Roo.data.JsonStore
14303  * Small helper class to make creating Stores for JSON data easier. <br/>
14304 <pre><code>
14305 var store = new Roo.data.JsonStore({
14306     url: 'get-images.php',
14307     root: 'images',
14308     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14309 });
14310 </code></pre>
14311  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14312  * JsonReader and HttpProxy (unless inline data is provided).</b>
14313  * @cfg {Array} fields An array of field definition objects, or field name strings.
14314  * @constructor
14315  * @param {Object} config
14316  */
14317 Roo.data.JsonStore = function(c){
14318     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14319         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14320         reader: new Roo.data.JsonReader(c, c.fields)
14321     }));
14322 };
14323 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14324  * Based on:
14325  * Ext JS Library 1.1.1
14326  * Copyright(c) 2006-2007, Ext JS, LLC.
14327  *
14328  * Originally Released Under LGPL - original licence link has changed is not relivant.
14329  *
14330  * Fork - LGPL
14331  * <script type="text/javascript">
14332  */
14333
14334  
14335 Roo.data.Field = function(config){
14336     if(typeof config == "string"){
14337         config = {name: config};
14338     }
14339     Roo.apply(this, config);
14340     
14341     if(!this.type){
14342         this.type = "auto";
14343     }
14344     
14345     var st = Roo.data.SortTypes;
14346     // named sortTypes are supported, here we look them up
14347     if(typeof this.sortType == "string"){
14348         this.sortType = st[this.sortType];
14349     }
14350     
14351     // set default sortType for strings and dates
14352     if(!this.sortType){
14353         switch(this.type){
14354             case "string":
14355                 this.sortType = st.asUCString;
14356                 break;
14357             case "date":
14358                 this.sortType = st.asDate;
14359                 break;
14360             default:
14361                 this.sortType = st.none;
14362         }
14363     }
14364
14365     // define once
14366     var stripRe = /[\$,%]/g;
14367
14368     // prebuilt conversion function for this field, instead of
14369     // switching every time we're reading a value
14370     if(!this.convert){
14371         var cv, dateFormat = this.dateFormat;
14372         switch(this.type){
14373             case "":
14374             case "auto":
14375             case undefined:
14376                 cv = function(v){ return v; };
14377                 break;
14378             case "string":
14379                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14380                 break;
14381             case "int":
14382                 cv = function(v){
14383                     return v !== undefined && v !== null && v !== '' ?
14384                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14385                     };
14386                 break;
14387             case "float":
14388                 cv = function(v){
14389                     return v !== undefined && v !== null && v !== '' ?
14390                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14391                     };
14392                 break;
14393             case "bool":
14394             case "boolean":
14395                 cv = function(v){ return v === true || v === "true" || v == 1; };
14396                 break;
14397             case "date":
14398                 cv = function(v){
14399                     if(!v){
14400                         return '';
14401                     }
14402                     if(v instanceof Date){
14403                         return v;
14404                     }
14405                     if(dateFormat){
14406                         if(dateFormat == "timestamp"){
14407                             return new Date(v*1000);
14408                         }
14409                         return Date.parseDate(v, dateFormat);
14410                     }
14411                     var parsed = Date.parse(v);
14412                     return parsed ? new Date(parsed) : null;
14413                 };
14414              break;
14415             
14416         }
14417         this.convert = cv;
14418     }
14419 };
14420
14421 Roo.data.Field.prototype = {
14422     dateFormat: null,
14423     defaultValue: "",
14424     mapping: null,
14425     sortType : null,
14426     sortDir : "ASC"
14427 };/*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437  
14438 // Base class for reading structured data from a data source.  This class is intended to be
14439 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14440
14441 /**
14442  * @class Roo.data.DataReader
14443  * Base class for reading structured data from a data source.  This class is intended to be
14444  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14445  */
14446
14447 Roo.data.DataReader = function(meta, recordType){
14448     
14449     this.meta = meta;
14450     
14451     this.recordType = recordType instanceof Array ? 
14452         Roo.data.Record.create(recordType) : recordType;
14453 };
14454
14455 Roo.data.DataReader.prototype = {
14456     
14457     
14458     readerType : 'Data',
14459      /**
14460      * Create an empty record
14461      * @param {Object} data (optional) - overlay some values
14462      * @return {Roo.data.Record} record created.
14463      */
14464     newRow :  function(d) {
14465         var da =  {};
14466         this.recordType.prototype.fields.each(function(c) {
14467             switch( c.type) {
14468                 case 'int' : da[c.name] = 0; break;
14469                 case 'date' : da[c.name] = new Date(); break;
14470                 case 'float' : da[c.name] = 0.0; break;
14471                 case 'boolean' : da[c.name] = false; break;
14472                 default : da[c.name] = ""; break;
14473             }
14474             
14475         });
14476         return new this.recordType(Roo.apply(da, d));
14477     }
14478     
14479     
14480 };/*
14481  * Based on:
14482  * Ext JS Library 1.1.1
14483  * Copyright(c) 2006-2007, Ext JS, LLC.
14484  *
14485  * Originally Released Under LGPL - original licence link has changed is not relivant.
14486  *
14487  * Fork - LGPL
14488  * <script type="text/javascript">
14489  */
14490
14491 /**
14492  * @class Roo.data.DataProxy
14493  * @extends Roo.data.Observable
14494  * This class is an abstract base class for implementations which provide retrieval of
14495  * unformatted data objects.<br>
14496  * <p>
14497  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14498  * (of the appropriate type which knows how to parse the data object) to provide a block of
14499  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14500  * <p>
14501  * Custom implementations must implement the load method as described in
14502  * {@link Roo.data.HttpProxy#load}.
14503  */
14504 Roo.data.DataProxy = function(){
14505     this.addEvents({
14506         /**
14507          * @event beforeload
14508          * Fires before a network request is made to retrieve a data object.
14509          * @param {Object} This DataProxy object.
14510          * @param {Object} params The params parameter to the load function.
14511          */
14512         beforeload : true,
14513         /**
14514          * @event load
14515          * Fires before the load method's callback is called.
14516          * @param {Object} This DataProxy object.
14517          * @param {Object} o The data object.
14518          * @param {Object} arg The callback argument object passed to the load function.
14519          */
14520         load : true,
14521         /**
14522          * @event loadexception
14523          * Fires if an Exception occurs during data retrieval.
14524          * @param {Object} This DataProxy object.
14525          * @param {Object} o The data object.
14526          * @param {Object} arg The callback argument object passed to the load function.
14527          * @param {Object} e The Exception.
14528          */
14529         loadexception : true
14530     });
14531     Roo.data.DataProxy.superclass.constructor.call(this);
14532 };
14533
14534 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14535
14536     /**
14537      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14538      */
14539 /*
14540  * Based on:
14541  * Ext JS Library 1.1.1
14542  * Copyright(c) 2006-2007, Ext JS, LLC.
14543  *
14544  * Originally Released Under LGPL - original licence link has changed is not relivant.
14545  *
14546  * Fork - LGPL
14547  * <script type="text/javascript">
14548  */
14549 /**
14550  * @class Roo.data.MemoryProxy
14551  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14552  * to the Reader when its load method is called.
14553  * @constructor
14554  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14555  */
14556 Roo.data.MemoryProxy = function(data){
14557     if (data.data) {
14558         data = data.data;
14559     }
14560     Roo.data.MemoryProxy.superclass.constructor.call(this);
14561     this.data = data;
14562 };
14563
14564 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14565     
14566     /**
14567      * Load data from the requested source (in this case an in-memory
14568      * data object passed to the constructor), read the data object into
14569      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14570      * process that block using the passed callback.
14571      * @param {Object} params This parameter is not used by the MemoryProxy class.
14572      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14573      * object into a block of Roo.data.Records.
14574      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14575      * The function must be passed <ul>
14576      * <li>The Record block object</li>
14577      * <li>The "arg" argument from the load function</li>
14578      * <li>A boolean success indicator</li>
14579      * </ul>
14580      * @param {Object} scope The scope in which to call the callback
14581      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14582      */
14583     load : function(params, reader, callback, scope, arg){
14584         params = params || {};
14585         var result;
14586         try {
14587             result = reader.readRecords(params.data ? params.data :this.data);
14588         }catch(e){
14589             this.fireEvent("loadexception", this, arg, null, e);
14590             callback.call(scope, null, arg, false);
14591             return;
14592         }
14593         callback.call(scope, result, arg, true);
14594     },
14595     
14596     // private
14597     update : function(params, records){
14598         
14599     }
14600 });/*
14601  * Based on:
14602  * Ext JS Library 1.1.1
14603  * Copyright(c) 2006-2007, Ext JS, LLC.
14604  *
14605  * Originally Released Under LGPL - original licence link has changed is not relivant.
14606  *
14607  * Fork - LGPL
14608  * <script type="text/javascript">
14609  */
14610 /**
14611  * @class Roo.data.HttpProxy
14612  * @extends Roo.data.DataProxy
14613  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14614  * configured to reference a certain URL.<br><br>
14615  * <p>
14616  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14617  * from which the running page was served.<br><br>
14618  * <p>
14619  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14620  * <p>
14621  * Be aware that to enable the browser to parse an XML document, the server must set
14622  * the Content-Type header in the HTTP response to "text/xml".
14623  * @constructor
14624  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14625  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14626  * will be used to make the request.
14627  */
14628 Roo.data.HttpProxy = function(conn){
14629     Roo.data.HttpProxy.superclass.constructor.call(this);
14630     // is conn a conn config or a real conn?
14631     this.conn = conn;
14632     this.useAjax = !conn || !conn.events;
14633   
14634 };
14635
14636 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14637     // thse are take from connection...
14638     
14639     /**
14640      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14641      */
14642     /**
14643      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14644      * extra parameters to each request made by this object. (defaults to undefined)
14645      */
14646     /**
14647      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14648      *  to each request made by this object. (defaults to undefined)
14649      */
14650     /**
14651      * @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)
14652      */
14653     /**
14654      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14655      */
14656      /**
14657      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14658      * @type Boolean
14659      */
14660   
14661
14662     /**
14663      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14664      * @type Boolean
14665      */
14666     /**
14667      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14668      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14669      * a finer-grained basis than the DataProxy events.
14670      */
14671     getConnection : function(){
14672         return this.useAjax ? Roo.Ajax : this.conn;
14673     },
14674
14675     /**
14676      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14677      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14678      * process that block using the passed callback.
14679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14680      * for the request to the remote server.
14681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14682      * object into a block of Roo.data.Records.
14683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14684      * The function must be passed <ul>
14685      * <li>The Record block object</li>
14686      * <li>The "arg" argument from the load function</li>
14687      * <li>A boolean success indicator</li>
14688      * </ul>
14689      * @param {Object} scope The scope in which to call the callback
14690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14691      */
14692     load : function(params, reader, callback, scope, arg){
14693         if(this.fireEvent("beforeload", this, params) !== false){
14694             var  o = {
14695                 params : params || {},
14696                 request: {
14697                     callback : callback,
14698                     scope : scope,
14699                     arg : arg
14700                 },
14701                 reader: reader,
14702                 callback : this.loadResponse,
14703                 scope: this
14704             };
14705             if(this.useAjax){
14706                 Roo.applyIf(o, this.conn);
14707                 if(this.activeRequest){
14708                     Roo.Ajax.abort(this.activeRequest);
14709                 }
14710                 this.activeRequest = Roo.Ajax.request(o);
14711             }else{
14712                 this.conn.request(o);
14713             }
14714         }else{
14715             callback.call(scope||this, null, arg, false);
14716         }
14717     },
14718
14719     // private
14720     loadResponse : function(o, success, response){
14721         delete this.activeRequest;
14722         if(!success){
14723             this.fireEvent("loadexception", this, o, response);
14724             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14725             return;
14726         }
14727         var result;
14728         try {
14729             result = o.reader.read(response);
14730         }catch(e){
14731             this.fireEvent("loadexception", this, o, response, e);
14732             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14733             return;
14734         }
14735         
14736         this.fireEvent("load", this, o, o.request.arg);
14737         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14738     },
14739
14740     // private
14741     update : function(dataSet){
14742
14743     },
14744
14745     // private
14746     updateResponse : function(dataSet){
14747
14748     }
14749 });/*
14750  * Based on:
14751  * Ext JS Library 1.1.1
14752  * Copyright(c) 2006-2007, Ext JS, LLC.
14753  *
14754  * Originally Released Under LGPL - original licence link has changed is not relivant.
14755  *
14756  * Fork - LGPL
14757  * <script type="text/javascript">
14758  */
14759
14760 /**
14761  * @class Roo.data.ScriptTagProxy
14762  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14763  * other than the originating domain of the running page.<br><br>
14764  * <p>
14765  * <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
14766  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14767  * <p>
14768  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14769  * source code that is used as the source inside a &lt;script> tag.<br><br>
14770  * <p>
14771  * In order for the browser to process the returned data, the server must wrap the data object
14772  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14773  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14774  * depending on whether the callback name was passed:
14775  * <p>
14776  * <pre><code>
14777 boolean scriptTag = false;
14778 String cb = request.getParameter("callback");
14779 if (cb != null) {
14780     scriptTag = true;
14781     response.setContentType("text/javascript");
14782 } else {
14783     response.setContentType("application/x-json");
14784 }
14785 Writer out = response.getWriter();
14786 if (scriptTag) {
14787     out.write(cb + "(");
14788 }
14789 out.print(dataBlock.toJsonString());
14790 if (scriptTag) {
14791     out.write(");");
14792 }
14793 </pre></code>
14794  *
14795  * @constructor
14796  * @param {Object} config A configuration object.
14797  */
14798 Roo.data.ScriptTagProxy = function(config){
14799     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14800     Roo.apply(this, config);
14801     this.head = document.getElementsByTagName("head")[0];
14802 };
14803
14804 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14805
14806 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14807     /**
14808      * @cfg {String} url The URL from which to request the data object.
14809      */
14810     /**
14811      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14812      */
14813     timeout : 30000,
14814     /**
14815      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14816      * the server the name of the callback function set up by the load call to process the returned data object.
14817      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14818      * javascript output which calls this named function passing the data object as its only parameter.
14819      */
14820     callbackParam : "callback",
14821     /**
14822      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14823      * name to the request.
14824      */
14825     nocache : true,
14826
14827     /**
14828      * Load data from the configured URL, read the data object into
14829      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14830      * process that block using the passed callback.
14831      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14832      * for the request to the remote server.
14833      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14834      * object into a block of Roo.data.Records.
14835      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14836      * The function must be passed <ul>
14837      * <li>The Record block object</li>
14838      * <li>The "arg" argument from the load function</li>
14839      * <li>A boolean success indicator</li>
14840      * </ul>
14841      * @param {Object} scope The scope in which to call the callback
14842      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14843      */
14844     load : function(params, reader, callback, scope, arg){
14845         if(this.fireEvent("beforeload", this, params) !== false){
14846
14847             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14848
14849             var url = this.url;
14850             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14851             if(this.nocache){
14852                 url += "&_dc=" + (new Date().getTime());
14853             }
14854             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14855             var trans = {
14856                 id : transId,
14857                 cb : "stcCallback"+transId,
14858                 scriptId : "stcScript"+transId,
14859                 params : params,
14860                 arg : arg,
14861                 url : url,
14862                 callback : callback,
14863                 scope : scope,
14864                 reader : reader
14865             };
14866             var conn = this;
14867
14868             window[trans.cb] = function(o){
14869                 conn.handleResponse(o, trans);
14870             };
14871
14872             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14873
14874             if(this.autoAbort !== false){
14875                 this.abort();
14876             }
14877
14878             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14879
14880             var script = document.createElement("script");
14881             script.setAttribute("src", url);
14882             script.setAttribute("type", "text/javascript");
14883             script.setAttribute("id", trans.scriptId);
14884             this.head.appendChild(script);
14885
14886             this.trans = trans;
14887         }else{
14888             callback.call(scope||this, null, arg, false);
14889         }
14890     },
14891
14892     // private
14893     isLoading : function(){
14894         return this.trans ? true : false;
14895     },
14896
14897     /**
14898      * Abort the current server request.
14899      */
14900     abort : function(){
14901         if(this.isLoading()){
14902             this.destroyTrans(this.trans);
14903         }
14904     },
14905
14906     // private
14907     destroyTrans : function(trans, isLoaded){
14908         this.head.removeChild(document.getElementById(trans.scriptId));
14909         clearTimeout(trans.timeoutId);
14910         if(isLoaded){
14911             window[trans.cb] = undefined;
14912             try{
14913                 delete window[trans.cb];
14914             }catch(e){}
14915         }else{
14916             // if hasn't been loaded, wait for load to remove it to prevent script error
14917             window[trans.cb] = function(){
14918                 window[trans.cb] = undefined;
14919                 try{
14920                     delete window[trans.cb];
14921                 }catch(e){}
14922             };
14923         }
14924     },
14925
14926     // private
14927     handleResponse : function(o, trans){
14928         this.trans = false;
14929         this.destroyTrans(trans, true);
14930         var result;
14931         try {
14932             result = trans.reader.readRecords(o);
14933         }catch(e){
14934             this.fireEvent("loadexception", this, o, trans.arg, e);
14935             trans.callback.call(trans.scope||window, null, trans.arg, false);
14936             return;
14937         }
14938         this.fireEvent("load", this, o, trans.arg);
14939         trans.callback.call(trans.scope||window, result, trans.arg, true);
14940     },
14941
14942     // private
14943     handleFailure : function(trans){
14944         this.trans = false;
14945         this.destroyTrans(trans, false);
14946         this.fireEvent("loadexception", this, null, trans.arg);
14947         trans.callback.call(trans.scope||window, null, trans.arg, false);
14948     }
14949 });/*
14950  * Based on:
14951  * Ext JS Library 1.1.1
14952  * Copyright(c) 2006-2007, Ext JS, LLC.
14953  *
14954  * Originally Released Under LGPL - original licence link has changed is not relivant.
14955  *
14956  * Fork - LGPL
14957  * <script type="text/javascript">
14958  */
14959
14960 /**
14961  * @class Roo.data.JsonReader
14962  * @extends Roo.data.DataReader
14963  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14964  * based on mappings in a provided Roo.data.Record constructor.
14965  * 
14966  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14967  * in the reply previously. 
14968  * 
14969  * <p>
14970  * Example code:
14971  * <pre><code>
14972 var RecordDef = Roo.data.Record.create([
14973     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14974     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14975 ]);
14976 var myReader = new Roo.data.JsonReader({
14977     totalProperty: "results",    // The property which contains the total dataset size (optional)
14978     root: "rows",                // The property which contains an Array of row objects
14979     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14980 }, RecordDef);
14981 </code></pre>
14982  * <p>
14983  * This would consume a JSON file like this:
14984  * <pre><code>
14985 { 'results': 2, 'rows': [
14986     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14987     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14988 }
14989 </code></pre>
14990  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14991  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14992  * paged from the remote server.
14993  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14994  * @cfg {String} root name of the property which contains the Array of row objects.
14995  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14996  * @cfg {Array} fields Array of field definition objects
14997  * @constructor
14998  * Create a new JsonReader
14999  * @param {Object} meta Metadata configuration options
15000  * @param {Object} recordType Either an Array of field definition objects,
15001  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15002  */
15003 Roo.data.JsonReader = function(meta, recordType){
15004     
15005     meta = meta || {};
15006     // set some defaults:
15007     Roo.applyIf(meta, {
15008         totalProperty: 'total',
15009         successProperty : 'success',
15010         root : 'data',
15011         id : 'id'
15012     });
15013     
15014     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15015 };
15016 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15017     
15018     readerType : 'Json',
15019     
15020     /**
15021      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15022      * Used by Store query builder to append _requestMeta to params.
15023      * 
15024      */
15025     metaFromRemote : false,
15026     /**
15027      * This method is only used by a DataProxy which has retrieved data from a remote server.
15028      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15029      * @return {Object} data A data block which is used by an Roo.data.Store object as
15030      * a cache of Roo.data.Records.
15031      */
15032     read : function(response){
15033         var json = response.responseText;
15034        
15035         var o = /* eval:var:o */ eval("("+json+")");
15036         if(!o) {
15037             throw {message: "JsonReader.read: Json object not found"};
15038         }
15039         
15040         if(o.metaData){
15041             
15042             delete this.ef;
15043             this.metaFromRemote = true;
15044             this.meta = o.metaData;
15045             this.recordType = Roo.data.Record.create(o.metaData.fields);
15046             this.onMetaChange(this.meta, this.recordType, o);
15047         }
15048         return this.readRecords(o);
15049     },
15050
15051     // private function a store will implement
15052     onMetaChange : function(meta, recordType, o){
15053
15054     },
15055
15056     /**
15057          * @ignore
15058          */
15059     simpleAccess: function(obj, subsc) {
15060         return obj[subsc];
15061     },
15062
15063         /**
15064          * @ignore
15065          */
15066     getJsonAccessor: function(){
15067         var re = /[\[\.]/;
15068         return function(expr) {
15069             try {
15070                 return(re.test(expr))
15071                     ? new Function("obj", "return obj." + expr)
15072                     : function(obj){
15073                         return obj[expr];
15074                     };
15075             } catch(e){}
15076             return Roo.emptyFn;
15077         };
15078     }(),
15079
15080     /**
15081      * Create a data block containing Roo.data.Records from an XML document.
15082      * @param {Object} o An object which contains an Array of row objects in the property specified
15083      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15084      * which contains the total size of the dataset.
15085      * @return {Object} data A data block which is used by an Roo.data.Store object as
15086      * a cache of Roo.data.Records.
15087      */
15088     readRecords : function(o){
15089         /**
15090          * After any data loads, the raw JSON data is available for further custom processing.
15091          * @type Object
15092          */
15093         this.o = o;
15094         var s = this.meta, Record = this.recordType,
15095             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15096
15097 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15098         if (!this.ef) {
15099             if(s.totalProperty) {
15100                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15101                 }
15102                 if(s.successProperty) {
15103                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15104                 }
15105                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15106                 if (s.id) {
15107                         var g = this.getJsonAccessor(s.id);
15108                         this.getId = function(rec) {
15109                                 var r = g(rec);  
15110                                 return (r === undefined || r === "") ? null : r;
15111                         };
15112                 } else {
15113                         this.getId = function(){return null;};
15114                 }
15115             this.ef = [];
15116             for(var jj = 0; jj < fl; jj++){
15117                 f = fi[jj];
15118                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15119                 this.ef[jj] = this.getJsonAccessor(map);
15120             }
15121         }
15122
15123         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15124         if(s.totalProperty){
15125             var vt = parseInt(this.getTotal(o), 10);
15126             if(!isNaN(vt)){
15127                 totalRecords = vt;
15128             }
15129         }
15130         if(s.successProperty){
15131             var vs = this.getSuccess(o);
15132             if(vs === false || vs === 'false'){
15133                 success = false;
15134             }
15135         }
15136         var records = [];
15137         for(var i = 0; i < c; i++){
15138                 var n = root[i];
15139             var values = {};
15140             var id = this.getId(n);
15141             for(var j = 0; j < fl; j++){
15142                 f = fi[j];
15143             var v = this.ef[j](n);
15144             if (!f.convert) {
15145                 Roo.log('missing convert for ' + f.name);
15146                 Roo.log(f);
15147                 continue;
15148             }
15149             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15150             }
15151             var record = new Record(values, id);
15152             record.json = n;
15153             records[i] = record;
15154         }
15155         return {
15156             raw : o,
15157             success : success,
15158             records : records,
15159             totalRecords : totalRecords
15160         };
15161     },
15162     // used when loading children.. @see loadDataFromChildren
15163     toLoadData: function(rec)
15164     {
15165         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15166         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15167         return { data : data, total : data.length };
15168         
15169     }
15170 });/*
15171  * Based on:
15172  * Ext JS Library 1.1.1
15173  * Copyright(c) 2006-2007, Ext JS, LLC.
15174  *
15175  * Originally Released Under LGPL - original licence link has changed is not relivant.
15176  *
15177  * Fork - LGPL
15178  * <script type="text/javascript">
15179  */
15180
15181 /**
15182  * @class Roo.data.ArrayReader
15183  * @extends Roo.data.DataReader
15184  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15185  * Each element of that Array represents a row of data fields. The
15186  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15187  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15188  * <p>
15189  * Example code:.
15190  * <pre><code>
15191 var RecordDef = Roo.data.Record.create([
15192     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15193     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15194 ]);
15195 var myReader = new Roo.data.ArrayReader({
15196     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15197 }, RecordDef);
15198 </code></pre>
15199  * <p>
15200  * This would consume an Array like this:
15201  * <pre><code>
15202 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15203   </code></pre>
15204  
15205  * @constructor
15206  * Create a new JsonReader
15207  * @param {Object} meta Metadata configuration options.
15208  * @param {Object|Array} recordType Either an Array of field definition objects
15209  * 
15210  * @cfg {Array} fields Array of field definition objects
15211  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15212  * as specified to {@link Roo.data.Record#create},
15213  * or an {@link Roo.data.Record} object
15214  *
15215  * 
15216  * created using {@link Roo.data.Record#create}.
15217  */
15218 Roo.data.ArrayReader = function(meta, recordType)
15219 {    
15220     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15221 };
15222
15223 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15224     
15225       /**
15226      * Create a data block containing Roo.data.Records from an XML document.
15227      * @param {Object} o An Array of row objects which represents the dataset.
15228      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15229      * a cache of Roo.data.Records.
15230      */
15231     readRecords : function(o)
15232     {
15233         var sid = this.meta ? this.meta.id : null;
15234         var recordType = this.recordType, fields = recordType.prototype.fields;
15235         var records = [];
15236         var root = o;
15237         for(var i = 0; i < root.length; i++){
15238             var n = root[i];
15239             var values = {};
15240             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15241             for(var j = 0, jlen = fields.length; j < jlen; j++){
15242                 var f = fields.items[j];
15243                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15244                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15245                 v = f.convert(v);
15246                 values[f.name] = v;
15247             }
15248             var record = new recordType(values, id);
15249             record.json = n;
15250             records[records.length] = record;
15251         }
15252         return {
15253             records : records,
15254             totalRecords : records.length
15255         };
15256     },
15257     // used when loading children.. @see loadDataFromChildren
15258     toLoadData: function(rec)
15259     {
15260         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15261         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15262         
15263     }
15264     
15265     
15266 });/*
15267  * - LGPL
15268  * * 
15269  */
15270
15271 /**
15272  * @class Roo.bootstrap.ComboBox
15273  * @extends Roo.bootstrap.TriggerField
15274  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15275  * @cfg {Boolean} append (true|false) default false
15276  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15277  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15278  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15279  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15280  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15281  * @cfg {Boolean} animate default true
15282  * @cfg {Boolean} emptyResultText only for touch device
15283  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15284  * @cfg {String} emptyTitle default ''
15285  * @cfg {Number} width fixed with? experimental
15286  * @constructor
15287  * Create a new ComboBox.
15288  * @param {Object} config Configuration options
15289  */
15290 Roo.bootstrap.ComboBox = function(config){
15291     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15292     this.addEvents({
15293         /**
15294          * @event expand
15295          * Fires when the dropdown list is expanded
15296         * @param {Roo.bootstrap.ComboBox} combo This combo box
15297         */
15298         'expand' : true,
15299         /**
15300          * @event collapse
15301          * Fires when the dropdown list is collapsed
15302         * @param {Roo.bootstrap.ComboBox} combo This combo box
15303         */
15304         'collapse' : true,
15305         /**
15306          * @event beforeselect
15307          * Fires before a list item is selected. Return false to cancel the selection.
15308         * @param {Roo.bootstrap.ComboBox} combo This combo box
15309         * @param {Roo.data.Record} record The data record returned from the underlying store
15310         * @param {Number} index The index of the selected item in the dropdown list
15311         */
15312         'beforeselect' : true,
15313         /**
15314          * @event select
15315          * Fires when a list item is selected
15316         * @param {Roo.bootstrap.ComboBox} combo This combo box
15317         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15318         * @param {Number} index The index of the selected item in the dropdown list
15319         */
15320         'select' : true,
15321         /**
15322          * @event beforequery
15323          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15324          * The event object passed has these properties:
15325         * @param {Roo.bootstrap.ComboBox} combo This combo box
15326         * @param {String} query The query
15327         * @param {Boolean} forceAll true to force "all" query
15328         * @param {Boolean} cancel true to cancel the query
15329         * @param {Object} e The query event object
15330         */
15331         'beforequery': true,
15332          /**
15333          * @event add
15334          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15335         * @param {Roo.bootstrap.ComboBox} combo This combo box
15336         */
15337         'add' : true,
15338         /**
15339          * @event edit
15340          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15341         * @param {Roo.bootstrap.ComboBox} combo This combo box
15342         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15343         */
15344         'edit' : true,
15345         /**
15346          * @event remove
15347          * Fires when the remove value from the combobox array
15348         * @param {Roo.bootstrap.ComboBox} combo This combo box
15349         */
15350         'remove' : true,
15351         /**
15352          * @event afterremove
15353          * Fires when the remove value from the combobox array
15354         * @param {Roo.bootstrap.ComboBox} combo This combo box
15355         */
15356         'afterremove' : true,
15357         /**
15358          * @event specialfilter
15359          * Fires when specialfilter
15360             * @param {Roo.bootstrap.ComboBox} combo This combo box
15361             */
15362         'specialfilter' : true,
15363         /**
15364          * @event tick
15365          * Fires when tick the element
15366             * @param {Roo.bootstrap.ComboBox} combo This combo box
15367             */
15368         'tick' : true,
15369         /**
15370          * @event touchviewdisplay
15371          * Fires when touch view require special display (default is using displayField)
15372             * @param {Roo.bootstrap.ComboBox} combo This combo box
15373             * @param {Object} cfg set html .
15374             */
15375         'touchviewdisplay' : true
15376         
15377     });
15378     
15379     this.item = [];
15380     this.tickItems = [];
15381     
15382     this.selectedIndex = -1;
15383     if(this.mode == 'local'){
15384         if(config.queryDelay === undefined){
15385             this.queryDelay = 10;
15386         }
15387         if(config.minChars === undefined){
15388             this.minChars = 0;
15389         }
15390     }
15391 };
15392
15393 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15394      
15395     /**
15396      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15397      * rendering into an Roo.Editor, defaults to false)
15398      */
15399     /**
15400      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15401      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15402      */
15403     /**
15404      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15405      */
15406     /**
15407      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15408      * the dropdown list (defaults to undefined, with no header element)
15409      */
15410
15411      /**
15412      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15413      */
15414      
15415      /**
15416      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15417      */
15418     listWidth: undefined,
15419     /**
15420      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15421      * mode = 'remote' or 'text' if mode = 'local')
15422      */
15423     displayField: undefined,
15424     
15425     /**
15426      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15427      * mode = 'remote' or 'value' if mode = 'local'). 
15428      * Note: use of a valueField requires the user make a selection
15429      * in order for a value to be mapped.
15430      */
15431     valueField: undefined,
15432     /**
15433      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15434      */
15435     modalTitle : '',
15436     
15437     /**
15438      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15439      * field's data value (defaults to the underlying DOM element's name)
15440      */
15441     hiddenName: undefined,
15442     /**
15443      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15444      */
15445     listClass: '',
15446     /**
15447      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15448      */
15449     selectedClass: 'active',
15450     
15451     /**
15452      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15453      */
15454     shadow:'sides',
15455     /**
15456      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15457      * anchor positions (defaults to 'tl-bl')
15458      */
15459     listAlign: 'tl-bl?',
15460     /**
15461      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15462      */
15463     maxHeight: 300,
15464     /**
15465      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15466      * query specified by the allQuery config option (defaults to 'query')
15467      */
15468     triggerAction: 'query',
15469     /**
15470      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15471      * (defaults to 4, does not apply if editable = false)
15472      */
15473     minChars : 4,
15474     /**
15475      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15476      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15477      */
15478     typeAhead: false,
15479     /**
15480      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15481      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15482      */
15483     queryDelay: 500,
15484     /**
15485      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15486      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15487      */
15488     pageSize: 0,
15489     /**
15490      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15491      * when editable = true (defaults to false)
15492      */
15493     selectOnFocus:false,
15494     /**
15495      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15496      */
15497     queryParam: 'query',
15498     /**
15499      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15500      * when mode = 'remote' (defaults to 'Loading...')
15501      */
15502     loadingText: 'Loading...',
15503     /**
15504      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15505      */
15506     resizable: false,
15507     /**
15508      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15509      */
15510     handleHeight : 8,
15511     /**
15512      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15513      * traditional select (defaults to true)
15514      */
15515     editable: true,
15516     /**
15517      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15518      */
15519     allQuery: '',
15520     /**
15521      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15522      */
15523     mode: 'remote',
15524     /**
15525      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15526      * listWidth has a higher value)
15527      */
15528     minListWidth : 70,
15529     /**
15530      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15531      * allow the user to set arbitrary text into the field (defaults to false)
15532      */
15533     forceSelection:false,
15534     /**
15535      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15536      * if typeAhead = true (defaults to 250)
15537      */
15538     typeAheadDelay : 250,
15539     /**
15540      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15541      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15542      */
15543     valueNotFoundText : undefined,
15544     /**
15545      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15546      */
15547     blockFocus : false,
15548     
15549     /**
15550      * @cfg {Boolean} disableClear Disable showing of clear button.
15551      */
15552     disableClear : false,
15553     /**
15554      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15555      */
15556     alwaysQuery : false,
15557     
15558     /**
15559      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15560      */
15561     multiple : false,
15562     
15563     /**
15564      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15565      */
15566     invalidClass : "has-warning",
15567     
15568     /**
15569      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15570      */
15571     validClass : "has-success",
15572     
15573     /**
15574      * @cfg {Boolean} specialFilter (true|false) special filter default false
15575      */
15576     specialFilter : false,
15577     
15578     /**
15579      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15580      */
15581     mobileTouchView : true,
15582     
15583     /**
15584      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15585      */
15586     useNativeIOS : false,
15587     
15588     /**
15589      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15590      */
15591     mobile_restrict_height : false,
15592     
15593     ios_options : false,
15594     
15595     //private
15596     addicon : false,
15597     editicon: false,
15598     
15599     page: 0,
15600     hasQuery: false,
15601     append: false,
15602     loadNext: false,
15603     autoFocus : true,
15604     tickable : false,
15605     btnPosition : 'right',
15606     triggerList : true,
15607     showToggleBtn : true,
15608     animate : true,
15609     emptyResultText: 'Empty',
15610     triggerText : 'Select',
15611     emptyTitle : '',
15612     width : false,
15613     
15614     // element that contains real text value.. (when hidden is used..)
15615     
15616     getAutoCreate : function()
15617     {   
15618         var cfg = false;
15619         //render
15620         /*
15621          * Render classic select for iso
15622          */
15623         
15624         if(Roo.isIOS && this.useNativeIOS){
15625             cfg = this.getAutoCreateNativeIOS();
15626             return cfg;
15627         }
15628         
15629         /*
15630          * Touch Devices
15631          */
15632         
15633         if(Roo.isTouch && this.mobileTouchView){
15634             cfg = this.getAutoCreateTouchView();
15635             return cfg;;
15636         }
15637         
15638         /*
15639          *  Normal ComboBox
15640          */
15641         if(!this.tickable){
15642             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15643             return cfg;
15644         }
15645         
15646         /*
15647          *  ComboBox with tickable selections
15648          */
15649              
15650         var align = this.labelAlign || this.parentLabelAlign();
15651         
15652         cfg = {
15653             cls : 'form-group roo-combobox-tickable' //input-group
15654         };
15655         
15656         var btn_text_select = '';
15657         var btn_text_done = '';
15658         var btn_text_cancel = '';
15659         
15660         if (this.btn_text_show) {
15661             btn_text_select = 'Select';
15662             btn_text_done = 'Done';
15663             btn_text_cancel = 'Cancel'; 
15664         }
15665         
15666         var buttons = {
15667             tag : 'div',
15668             cls : 'tickable-buttons',
15669             cn : [
15670                 {
15671                     tag : 'button',
15672                     type : 'button',
15673                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15674                     //html : this.triggerText
15675                     html: btn_text_select
15676                 },
15677                 {
15678                     tag : 'button',
15679                     type : 'button',
15680                     name : 'ok',
15681                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15682                     //html : 'Done'
15683                     html: btn_text_done
15684                 },
15685                 {
15686                     tag : 'button',
15687                     type : 'button',
15688                     name : 'cancel',
15689                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15690                     //html : 'Cancel'
15691                     html: btn_text_cancel
15692                 }
15693             ]
15694         };
15695         
15696         if(this.editable){
15697             buttons.cn.unshift({
15698                 tag: 'input',
15699                 cls: 'roo-select2-search-field-input'
15700             });
15701         }
15702         
15703         var _this = this;
15704         
15705         Roo.each(buttons.cn, function(c){
15706             if (_this.size) {
15707                 c.cls += ' btn-' + _this.size;
15708             }
15709
15710             if (_this.disabled) {
15711                 c.disabled = true;
15712             }
15713         });
15714         
15715         var box = {
15716             tag: 'div',
15717             style : 'display: contents',
15718             cn: [
15719                 {
15720                     tag: 'input',
15721                     type : 'hidden',
15722                     cls: 'form-hidden-field'
15723                 },
15724                 {
15725                     tag: 'ul',
15726                     cls: 'roo-select2-choices',
15727                     cn:[
15728                         {
15729                             tag: 'li',
15730                             cls: 'roo-select2-search-field',
15731                             cn: [
15732                                 buttons
15733                             ]
15734                         }
15735                     ]
15736                 }
15737             ]
15738         };
15739         
15740         var combobox = {
15741             cls: 'roo-select2-container input-group roo-select2-container-multi',
15742             cn: [
15743                 
15744                 box
15745 //                {
15746 //                    tag: 'ul',
15747 //                    cls: 'typeahead typeahead-long dropdown-menu',
15748 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15749 //                }
15750             ]
15751         };
15752         
15753         if(this.hasFeedback && !this.allowBlank){
15754             
15755             var feedback = {
15756                 tag: 'span',
15757                 cls: 'glyphicon form-control-feedback'
15758             };
15759
15760             combobox.cn.push(feedback);
15761         }
15762         
15763         
15764         
15765         var indicator = {
15766             tag : 'i',
15767             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15768             tooltip : 'This field is required'
15769         };
15770         if (Roo.bootstrap.version == 4) {
15771             indicator = {
15772                 tag : 'i',
15773                 style : 'display:none'
15774             };
15775         }
15776         if (align ==='left' && this.fieldLabel.length) {
15777             
15778             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15779             
15780             cfg.cn = [
15781                 indicator,
15782                 {
15783                     tag: 'label',
15784                     'for' :  id,
15785                     cls : 'control-label col-form-label',
15786                     html : this.fieldLabel
15787
15788                 },
15789                 {
15790                     cls : "", 
15791                     cn: [
15792                         combobox
15793                     ]
15794                 }
15795
15796             ];
15797             
15798             var labelCfg = cfg.cn[1];
15799             var contentCfg = cfg.cn[2];
15800             
15801
15802             if(this.indicatorpos == 'right'){
15803                 
15804                 cfg.cn = [
15805                     {
15806                         tag: 'label',
15807                         'for' :  id,
15808                         cls : 'control-label col-form-label',
15809                         cn : [
15810                             {
15811                                 tag : 'span',
15812                                 html : this.fieldLabel
15813                             },
15814                             indicator
15815                         ]
15816                     },
15817                     {
15818                         cls : "",
15819                         cn: [
15820                             combobox
15821                         ]
15822                     }
15823
15824                 ];
15825                 
15826                 
15827                 
15828                 labelCfg = cfg.cn[0];
15829                 contentCfg = cfg.cn[1];
15830             
15831             }
15832             
15833             if(this.labelWidth > 12){
15834                 labelCfg.style = "width: " + this.labelWidth + 'px';
15835             }
15836             if(this.width * 1 > 0){
15837                 contentCfg.style = "width: " + this.width + 'px';
15838             }
15839             if(this.labelWidth < 13 && this.labelmd == 0){
15840                 this.labelmd = this.labelWidth;
15841             }
15842             
15843             if(this.labellg > 0){
15844                 labelCfg.cls += ' col-lg-' + this.labellg;
15845                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15846             }
15847             
15848             if(this.labelmd > 0){
15849                 labelCfg.cls += ' col-md-' + this.labelmd;
15850                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15851             }
15852             
15853             if(this.labelsm > 0){
15854                 labelCfg.cls += ' col-sm-' + this.labelsm;
15855                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15856             }
15857             
15858             if(this.labelxs > 0){
15859                 labelCfg.cls += ' col-xs-' + this.labelxs;
15860                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15861             }
15862                 
15863                 
15864         } else if ( this.fieldLabel.length) {
15865 //                Roo.log(" label");
15866                  cfg.cn = [
15867                    indicator,
15868                     {
15869                         tag: 'label',
15870                         //cls : 'input-group-addon',
15871                         html : this.fieldLabel
15872                     },
15873                     combobox
15874                 ];
15875                 
15876                 if(this.indicatorpos == 'right'){
15877                     cfg.cn = [
15878                         {
15879                             tag: 'label',
15880                             //cls : 'input-group-addon',
15881                             html : this.fieldLabel
15882                         },
15883                         indicator,
15884                         combobox
15885                     ];
15886                     
15887                 }
15888
15889         } else {
15890             
15891 //                Roo.log(" no label && no align");
15892                 cfg = combobox
15893                      
15894                 
15895         }
15896          
15897         var settings=this;
15898         ['xs','sm','md','lg'].map(function(size){
15899             if (settings[size]) {
15900                 cfg.cls += ' col-' + size + '-' + settings[size];
15901             }
15902         });
15903         
15904         return cfg;
15905         
15906     },
15907     
15908     _initEventsCalled : false,
15909     
15910     // private
15911     initEvents: function()
15912     {   
15913         if (this._initEventsCalled) { // as we call render... prevent looping...
15914             return;
15915         }
15916         this._initEventsCalled = true;
15917         
15918         if (!this.store) {
15919             throw "can not find store for combo";
15920         }
15921         
15922         this.indicator = this.indicatorEl();
15923         
15924         this.store = Roo.factory(this.store, Roo.data);
15925         this.store.parent = this;
15926         
15927         // if we are building from html. then this element is so complex, that we can not really
15928         // use the rendered HTML.
15929         // so we have to trash and replace the previous code.
15930         if (Roo.XComponent.build_from_html) {
15931             // remove this element....
15932             var e = this.el.dom, k=0;
15933             while (e ) { e = e.previousSibling;  ++k;}
15934
15935             this.el.remove();
15936             
15937             this.el=false;
15938             this.rendered = false;
15939             
15940             this.render(this.parent().getChildContainer(true), k);
15941         }
15942         
15943         if(Roo.isIOS && this.useNativeIOS){
15944             this.initIOSView();
15945             return;
15946         }
15947         
15948         /*
15949          * Touch Devices
15950          */
15951         
15952         if(Roo.isTouch && this.mobileTouchView){
15953             this.initTouchView();
15954             return;
15955         }
15956         
15957         if(this.tickable){
15958             this.initTickableEvents();
15959             return;
15960         }
15961         
15962         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15963         
15964         if(this.hiddenName){
15965             
15966             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15967             
15968             this.hiddenField.dom.value =
15969                 this.hiddenValue !== undefined ? this.hiddenValue :
15970                 this.value !== undefined ? this.value : '';
15971
15972             // prevent input submission
15973             this.el.dom.removeAttribute('name');
15974             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15975              
15976              
15977         }
15978         //if(Roo.isGecko){
15979         //    this.el.dom.setAttribute('autocomplete', 'off');
15980         //}
15981         
15982         var cls = 'x-combo-list';
15983         
15984         //this.list = new Roo.Layer({
15985         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15986         //});
15987         
15988         var _this = this;
15989         
15990         (function(){
15991             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15992             _this.list.setWidth(lw);
15993         }).defer(100);
15994         
15995         this.list.on('mouseover', this.onViewOver, this);
15996         this.list.on('mousemove', this.onViewMove, this);
15997         this.list.on('scroll', this.onViewScroll, this);
15998         
15999         /*
16000         this.list.swallowEvent('mousewheel');
16001         this.assetHeight = 0;
16002
16003         if(this.title){
16004             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16005             this.assetHeight += this.header.getHeight();
16006         }
16007
16008         this.innerList = this.list.createChild({cls:cls+'-inner'});
16009         this.innerList.on('mouseover', this.onViewOver, this);
16010         this.innerList.on('mousemove', this.onViewMove, this);
16011         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16012         
16013         if(this.allowBlank && !this.pageSize && !this.disableClear){
16014             this.footer = this.list.createChild({cls:cls+'-ft'});
16015             this.pageTb = new Roo.Toolbar(this.footer);
16016            
16017         }
16018         if(this.pageSize){
16019             this.footer = this.list.createChild({cls:cls+'-ft'});
16020             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16021                     {pageSize: this.pageSize});
16022             
16023         }
16024         
16025         if (this.pageTb && this.allowBlank && !this.disableClear) {
16026             var _this = this;
16027             this.pageTb.add(new Roo.Toolbar.Fill(), {
16028                 cls: 'x-btn-icon x-btn-clear',
16029                 text: '&#160;',
16030                 handler: function()
16031                 {
16032                     _this.collapse();
16033                     _this.clearValue();
16034                     _this.onSelect(false, -1);
16035                 }
16036             });
16037         }
16038         if (this.footer) {
16039             this.assetHeight += this.footer.getHeight();
16040         }
16041         */
16042             
16043         if(!this.tpl){
16044             this.tpl = Roo.bootstrap.version == 4 ?
16045                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16046                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16047         }
16048
16049         this.view = new Roo.View(this.list, this.tpl, {
16050             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16051         });
16052         //this.view.wrapEl.setDisplayed(false);
16053         this.view.on('click', this.onViewClick, this);
16054         
16055         
16056         this.store.on('beforeload', this.onBeforeLoad, this);
16057         this.store.on('load', this.onLoad, this);
16058         this.store.on('loadexception', this.onLoadException, this);
16059         /*
16060         if(this.resizable){
16061             this.resizer = new Roo.Resizable(this.list,  {
16062                pinned:true, handles:'se'
16063             });
16064             this.resizer.on('resize', function(r, w, h){
16065                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16066                 this.listWidth = w;
16067                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16068                 this.restrictHeight();
16069             }, this);
16070             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16071         }
16072         */
16073         if(!this.editable){
16074             this.editable = true;
16075             this.setEditable(false);
16076         }
16077         
16078         /*
16079         
16080         if (typeof(this.events.add.listeners) != 'undefined') {
16081             
16082             this.addicon = this.wrap.createChild(
16083                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16084        
16085             this.addicon.on('click', function(e) {
16086                 this.fireEvent('add', this);
16087             }, this);
16088         }
16089         if (typeof(this.events.edit.listeners) != 'undefined') {
16090             
16091             this.editicon = this.wrap.createChild(
16092                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16093             if (this.addicon) {
16094                 this.editicon.setStyle('margin-left', '40px');
16095             }
16096             this.editicon.on('click', function(e) {
16097                 
16098                 // we fire even  if inothing is selected..
16099                 this.fireEvent('edit', this, this.lastData );
16100                 
16101             }, this);
16102         }
16103         */
16104         
16105         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16106             "up" : function(e){
16107                 this.inKeyMode = true;
16108                 this.selectPrev();
16109             },
16110
16111             "down" : function(e){
16112                 if(!this.isExpanded()){
16113                     this.onTriggerClick();
16114                 }else{
16115                     this.inKeyMode = true;
16116                     this.selectNext();
16117                 }
16118             },
16119
16120             "enter" : function(e){
16121 //                this.onViewClick();
16122                 //return true;
16123                 this.collapse();
16124                 
16125                 if(this.fireEvent("specialkey", this, e)){
16126                     this.onViewClick(false);
16127                 }
16128                 
16129                 return true;
16130             },
16131
16132             "esc" : function(e){
16133                 this.collapse();
16134             },
16135
16136             "tab" : function(e){
16137                 this.collapse();
16138                 
16139                 if(this.fireEvent("specialkey", this, e)){
16140                     this.onViewClick(false);
16141                 }
16142                 
16143                 return true;
16144             },
16145
16146             scope : this,
16147
16148             doRelay : function(foo, bar, hname){
16149                 if(hname == 'down' || this.scope.isExpanded()){
16150                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16151                 }
16152                 return true;
16153             },
16154
16155             forceKeyDown: true
16156         });
16157         
16158         
16159         this.queryDelay = Math.max(this.queryDelay || 10,
16160                 this.mode == 'local' ? 10 : 250);
16161         
16162         
16163         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16164         
16165         if(this.typeAhead){
16166             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16167         }
16168         if(this.editable !== false){
16169             this.inputEl().on("keyup", this.onKeyUp, this);
16170         }
16171         if(this.forceSelection){
16172             this.inputEl().on('blur', this.doForce, this);
16173         }
16174         
16175         if(this.multiple){
16176             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16177             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16178         }
16179     },
16180     
16181     initTickableEvents: function()
16182     {   
16183         this.createList();
16184         
16185         if(this.hiddenName){
16186             
16187             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16188             
16189             this.hiddenField.dom.value =
16190                 this.hiddenValue !== undefined ? this.hiddenValue :
16191                 this.value !== undefined ? this.value : '';
16192
16193             // prevent input submission
16194             this.el.dom.removeAttribute('name');
16195             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16196              
16197              
16198         }
16199         
16200 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16201         
16202         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16203         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16204         if(this.triggerList){
16205             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16206         }
16207          
16208         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16209         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16210         
16211         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16212         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16213         
16214         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16215         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16216         
16217         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16218         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16219         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16220         
16221         this.okBtn.hide();
16222         this.cancelBtn.hide();
16223         
16224         var _this = this;
16225         
16226         (function(){
16227             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16228             _this.list.setWidth(lw);
16229         }).defer(100);
16230         
16231         this.list.on('mouseover', this.onViewOver, this);
16232         this.list.on('mousemove', this.onViewMove, this);
16233         
16234         this.list.on('scroll', this.onViewScroll, this);
16235         
16236         if(!this.tpl){
16237             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16238                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16239         }
16240
16241         this.view = new Roo.View(this.list, this.tpl, {
16242             singleSelect:true,
16243             tickable:true,
16244             parent:this,
16245             store: this.store,
16246             selectedClass: this.selectedClass
16247         });
16248         
16249         //this.view.wrapEl.setDisplayed(false);
16250         this.view.on('click', this.onViewClick, this);
16251         
16252         
16253         
16254         this.store.on('beforeload', this.onBeforeLoad, this);
16255         this.store.on('load', this.onLoad, this);
16256         this.store.on('loadexception', this.onLoadException, this);
16257         
16258         if(this.editable){
16259             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16260                 "up" : function(e){
16261                     this.inKeyMode = true;
16262                     this.selectPrev();
16263                 },
16264
16265                 "down" : function(e){
16266                     this.inKeyMode = true;
16267                     this.selectNext();
16268                 },
16269
16270                 "enter" : function(e){
16271                     if(this.fireEvent("specialkey", this, e)){
16272                         this.onViewClick(false);
16273                     }
16274                     
16275                     return true;
16276                 },
16277
16278                 "esc" : function(e){
16279                     this.onTickableFooterButtonClick(e, false, false);
16280                 },
16281
16282                 "tab" : function(e){
16283                     this.fireEvent("specialkey", this, e);
16284                     
16285                     this.onTickableFooterButtonClick(e, false, false);
16286                     
16287                     return true;
16288                 },
16289
16290                 scope : this,
16291
16292                 doRelay : function(e, fn, key){
16293                     if(this.scope.isExpanded()){
16294                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16295                     }
16296                     return true;
16297                 },
16298
16299                 forceKeyDown: true
16300             });
16301         }
16302         
16303         this.queryDelay = Math.max(this.queryDelay || 10,
16304                 this.mode == 'local' ? 10 : 250);
16305         
16306         
16307         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16308         
16309         if(this.typeAhead){
16310             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16311         }
16312         
16313         if(this.editable !== false){
16314             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16315         }
16316         
16317         this.indicator = this.indicatorEl();
16318         
16319         if(this.indicator){
16320             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16321             this.indicator.hide();
16322         }
16323         
16324     },
16325
16326     onDestroy : function(){
16327         if(this.view){
16328             this.view.setStore(null);
16329             this.view.el.removeAllListeners();
16330             this.view.el.remove();
16331             this.view.purgeListeners();
16332         }
16333         if(this.list){
16334             this.list.dom.innerHTML  = '';
16335         }
16336         
16337         if(this.store){
16338             this.store.un('beforeload', this.onBeforeLoad, this);
16339             this.store.un('load', this.onLoad, this);
16340             this.store.un('loadexception', this.onLoadException, this);
16341         }
16342         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16343     },
16344
16345     // private
16346     fireKey : function(e){
16347         if(e.isNavKeyPress() && !this.list.isVisible()){
16348             this.fireEvent("specialkey", this, e);
16349         }
16350     },
16351
16352     // private
16353     onResize: function(w, h)
16354     {
16355         
16356         
16357 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16358 //        
16359 //        if(typeof w != 'number'){
16360 //            // we do not handle it!?!?
16361 //            return;
16362 //        }
16363 //        var tw = this.trigger.getWidth();
16364 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16365 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16366 //        var x = w - tw;
16367 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16368 //            
16369 //        //this.trigger.setStyle('left', x+'px');
16370 //        
16371 //        if(this.list && this.listWidth === undefined){
16372 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16373 //            this.list.setWidth(lw);
16374 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16375 //        }
16376         
16377     
16378         
16379     },
16380
16381     /**
16382      * Allow or prevent the user from directly editing the field text.  If false is passed,
16383      * the user will only be able to select from the items defined in the dropdown list.  This method
16384      * is the runtime equivalent of setting the 'editable' config option at config time.
16385      * @param {Boolean} value True to allow the user to directly edit the field text
16386      */
16387     setEditable : function(value){
16388         if(value == this.editable){
16389             return;
16390         }
16391         this.editable = value;
16392         if(!value){
16393             this.inputEl().dom.setAttribute('readOnly', true);
16394             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16395             this.inputEl().addClass('x-combo-noedit');
16396         }else{
16397             this.inputEl().dom.removeAttribute('readOnly');
16398             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16399             this.inputEl().removeClass('x-combo-noedit');
16400         }
16401     },
16402
16403     // private
16404     
16405     onBeforeLoad : function(combo,opts){
16406         if(!this.hasFocus){
16407             return;
16408         }
16409          if (!opts.add) {
16410             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16411          }
16412         this.restrictHeight();
16413         this.selectedIndex = -1;
16414     },
16415
16416     // private
16417     onLoad : function(){
16418         
16419         this.hasQuery = false;
16420         
16421         if(!this.hasFocus){
16422             return;
16423         }
16424         
16425         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16426             this.loading.hide();
16427         }
16428         
16429         if(this.store.getCount() > 0){
16430             
16431             this.expand();
16432             this.restrictHeight();
16433             if(this.lastQuery == this.allQuery){
16434                 if(this.editable && !this.tickable){
16435                     this.inputEl().dom.select();
16436                 }
16437                 
16438                 if(
16439                     !this.selectByValue(this.value, true) &&
16440                     this.autoFocus && 
16441                     (
16442                         !this.store.lastOptions ||
16443                         typeof(this.store.lastOptions.add) == 'undefined' || 
16444                         this.store.lastOptions.add != true
16445                     )
16446                 ){
16447                     this.select(0, true);
16448                 }
16449             }else{
16450                 if(this.autoFocus){
16451                     this.selectNext();
16452                 }
16453                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16454                     this.taTask.delay(this.typeAheadDelay);
16455                 }
16456             }
16457         }else{
16458             this.onEmptyResults();
16459         }
16460         
16461         //this.el.focus();
16462     },
16463     // private
16464     onLoadException : function()
16465     {
16466         this.hasQuery = false;
16467         
16468         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16469             this.loading.hide();
16470         }
16471         
16472         if(this.tickable && this.editable){
16473             return;
16474         }
16475         
16476         this.collapse();
16477         // only causes errors at present
16478         //Roo.log(this.store.reader.jsonData);
16479         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16480             // fixme
16481             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16482         //}
16483         
16484         
16485     },
16486     // private
16487     onTypeAhead : function(){
16488         if(this.store.getCount() > 0){
16489             var r = this.store.getAt(0);
16490             var newValue = r.data[this.displayField];
16491             var len = newValue.length;
16492             var selStart = this.getRawValue().length;
16493             
16494             if(selStart != len){
16495                 this.setRawValue(newValue);
16496                 this.selectText(selStart, newValue.length);
16497             }
16498         }
16499     },
16500
16501     // private
16502     onSelect : function(record, index){
16503         
16504         if(this.fireEvent('beforeselect', this, record, index) !== false){
16505         
16506             this.setFromData(index > -1 ? record.data : false);
16507             
16508             this.collapse();
16509             this.fireEvent('select', this, record, index);
16510         }
16511     },
16512
16513     /**
16514      * Returns the currently selected field value or empty string if no value is set.
16515      * @return {String} value The selected value
16516      */
16517     getValue : function()
16518     {
16519         if(Roo.isIOS && this.useNativeIOS){
16520             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16521         }
16522         
16523         if(this.multiple){
16524             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16525         }
16526         
16527         if(this.valueField){
16528             return typeof this.value != 'undefined' ? this.value : '';
16529         }else{
16530             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16531         }
16532     },
16533     
16534     getRawValue : function()
16535     {
16536         if(Roo.isIOS && this.useNativeIOS){
16537             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16538         }
16539         
16540         var v = this.inputEl().getValue();
16541         
16542         return v;
16543     },
16544
16545     /**
16546      * Clears any text/value currently set in the field
16547      */
16548     clearValue : function(){
16549         
16550         if(this.hiddenField){
16551             this.hiddenField.dom.value = '';
16552         }
16553         this.value = '';
16554         this.setRawValue('');
16555         this.lastSelectionText = '';
16556         this.lastData = false;
16557         
16558         var close = this.closeTriggerEl();
16559         
16560         if(close){
16561             close.hide();
16562         }
16563         
16564         this.validate();
16565         
16566     },
16567
16568     /**
16569      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16570      * will be displayed in the field.  If the value does not match the data value of an existing item,
16571      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16572      * Otherwise the field will be blank (although the value will still be set).
16573      * @param {String} value The value to match
16574      */
16575     setValue : function(v)
16576     {
16577         if(Roo.isIOS && this.useNativeIOS){
16578             this.setIOSValue(v);
16579             return;
16580         }
16581         
16582         if(this.multiple){
16583             this.syncValue();
16584             return;
16585         }
16586         
16587         var text = v;
16588         if(this.valueField){
16589             var r = this.findRecord(this.valueField, v);
16590             if(r){
16591                 text = r.data[this.displayField];
16592             }else if(this.valueNotFoundText !== undefined){
16593                 text = this.valueNotFoundText;
16594             }
16595         }
16596         this.lastSelectionText = text;
16597         if(this.hiddenField){
16598             this.hiddenField.dom.value = v;
16599         }
16600         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16601         this.value = v;
16602         
16603         var close = this.closeTriggerEl();
16604         
16605         if(close){
16606             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16607         }
16608         
16609         this.validate();
16610     },
16611     /**
16612      * @property {Object} the last set data for the element
16613      */
16614     
16615     lastData : false,
16616     /**
16617      * Sets the value of the field based on a object which is related to the record format for the store.
16618      * @param {Object} value the value to set as. or false on reset?
16619      */
16620     setFromData : function(o){
16621         
16622         if(this.multiple){
16623             this.addItem(o);
16624             return;
16625         }
16626             
16627         var dv = ''; // display value
16628         var vv = ''; // value value..
16629         this.lastData = o;
16630         if (this.displayField) {
16631             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16632         } else {
16633             // this is an error condition!!!
16634             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16635         }
16636         
16637         if(this.valueField){
16638             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16639         }
16640         
16641         var close = this.closeTriggerEl();
16642         
16643         if(close){
16644             if(dv.length || vv * 1 > 0){
16645                 close.show() ;
16646                 this.blockFocus=true;
16647             } else {
16648                 close.hide();
16649             }             
16650         }
16651         
16652         if(this.hiddenField){
16653             this.hiddenField.dom.value = vv;
16654             
16655             this.lastSelectionText = dv;
16656             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16657             this.value = vv;
16658             return;
16659         }
16660         // no hidden field.. - we store the value in 'value', but still display
16661         // display field!!!!
16662         this.lastSelectionText = dv;
16663         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16664         this.value = vv;
16665         
16666         
16667         
16668     },
16669     // private
16670     reset : function(){
16671         // overridden so that last data is reset..
16672         
16673         if(this.multiple){
16674             this.clearItem();
16675             return;
16676         }
16677         
16678         this.setValue(this.originalValue);
16679         //this.clearInvalid();
16680         this.lastData = false;
16681         if (this.view) {
16682             this.view.clearSelections();
16683         }
16684         
16685         this.validate();
16686     },
16687     // private
16688     findRecord : function(prop, value){
16689         var record;
16690         if(this.store.getCount() > 0){
16691             this.store.each(function(r){
16692                 if(r.data[prop] == value){
16693                     record = r;
16694                     return false;
16695                 }
16696                 return true;
16697             });
16698         }
16699         return record;
16700     },
16701     
16702     getName: function()
16703     {
16704         // returns hidden if it's set..
16705         if (!this.rendered) {return ''};
16706         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16707         
16708     },
16709     // private
16710     onViewMove : function(e, t){
16711         this.inKeyMode = false;
16712     },
16713
16714     // private
16715     onViewOver : function(e, t){
16716         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16717             return;
16718         }
16719         var item = this.view.findItemFromChild(t);
16720         
16721         if(item){
16722             var index = this.view.indexOf(item);
16723             this.select(index, false);
16724         }
16725     },
16726
16727     // private
16728     onViewClick : function(view, doFocus, el, e)
16729     {
16730         var index = this.view.getSelectedIndexes()[0];
16731         
16732         var r = this.store.getAt(index);
16733         
16734         if(this.tickable){
16735             
16736             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16737                 return;
16738             }
16739             
16740             var rm = false;
16741             var _this = this;
16742             
16743             Roo.each(this.tickItems, function(v,k){
16744                 
16745                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16746                     Roo.log(v);
16747                     _this.tickItems.splice(k, 1);
16748                     
16749                     if(typeof(e) == 'undefined' && view == false){
16750                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16751                     }
16752                     
16753                     rm = true;
16754                     return;
16755                 }
16756             });
16757             
16758             if(rm){
16759                 return;
16760             }
16761             
16762             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16763                 this.tickItems.push(r.data);
16764             }
16765             
16766             if(typeof(e) == 'undefined' && view == false){
16767                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16768             }
16769                     
16770             return;
16771         }
16772         
16773         if(r){
16774             this.onSelect(r, index);
16775         }
16776         if(doFocus !== false && !this.blockFocus){
16777             this.inputEl().focus();
16778         }
16779     },
16780
16781     // private
16782     restrictHeight : function(){
16783         //this.innerList.dom.style.height = '';
16784         //var inner = this.innerList.dom;
16785         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16786         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16787         //this.list.beginUpdate();
16788         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16789         this.list.alignTo(this.inputEl(), this.listAlign);
16790         this.list.alignTo(this.inputEl(), this.listAlign);
16791         //this.list.endUpdate();
16792     },
16793
16794     // private
16795     onEmptyResults : function(){
16796         
16797         if(this.tickable && this.editable){
16798             this.hasFocus = false;
16799             this.restrictHeight();
16800             return;
16801         }
16802         
16803         this.collapse();
16804     },
16805
16806     /**
16807      * Returns true if the dropdown list is expanded, else false.
16808      */
16809     isExpanded : function(){
16810         return this.list.isVisible();
16811     },
16812
16813     /**
16814      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16815      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16816      * @param {String} value The data value of the item to select
16817      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16818      * selected item if it is not currently in view (defaults to true)
16819      * @return {Boolean} True if the value matched an item in the list, else false
16820      */
16821     selectByValue : function(v, scrollIntoView){
16822         if(v !== undefined && v !== null){
16823             var r = this.findRecord(this.valueField || this.displayField, v);
16824             if(r){
16825                 this.select(this.store.indexOf(r), scrollIntoView);
16826                 return true;
16827             }
16828         }
16829         return false;
16830     },
16831
16832     /**
16833      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16834      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16835      * @param {Number} index The zero-based index of the list item to select
16836      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16837      * selected item if it is not currently in view (defaults to true)
16838      */
16839     select : function(index, scrollIntoView){
16840         this.selectedIndex = index;
16841         this.view.select(index);
16842         if(scrollIntoView !== false){
16843             var el = this.view.getNode(index);
16844             /*
16845              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16846              */
16847             if(el){
16848                 this.list.scrollChildIntoView(el, false);
16849             }
16850         }
16851     },
16852
16853     // private
16854     selectNext : function(){
16855         var ct = this.store.getCount();
16856         if(ct > 0){
16857             if(this.selectedIndex == -1){
16858                 this.select(0);
16859             }else if(this.selectedIndex < ct-1){
16860                 this.select(this.selectedIndex+1);
16861             }
16862         }
16863     },
16864
16865     // private
16866     selectPrev : function(){
16867         var ct = this.store.getCount();
16868         if(ct > 0){
16869             if(this.selectedIndex == -1){
16870                 this.select(0);
16871             }else if(this.selectedIndex != 0){
16872                 this.select(this.selectedIndex-1);
16873             }
16874         }
16875     },
16876
16877     // private
16878     onKeyUp : function(e){
16879         if(this.editable !== false && !e.isSpecialKey()){
16880             this.lastKey = e.getKey();
16881             this.dqTask.delay(this.queryDelay);
16882         }
16883     },
16884
16885     // private
16886     validateBlur : function(){
16887         return !this.list || !this.list.isVisible();   
16888     },
16889
16890     // private
16891     initQuery : function(){
16892         
16893         var v = this.getRawValue();
16894         
16895         if(this.tickable && this.editable){
16896             v = this.tickableInputEl().getValue();
16897         }
16898         
16899         this.doQuery(v);
16900     },
16901
16902     // private
16903     doForce : function(){
16904         if(this.inputEl().dom.value.length > 0){
16905             this.inputEl().dom.value =
16906                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16907              
16908         }
16909     },
16910
16911     /**
16912      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16913      * query allowing the query action to be canceled if needed.
16914      * @param {String} query The SQL query to execute
16915      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16916      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16917      * saved in the current store (defaults to false)
16918      */
16919     doQuery : function(q, forceAll){
16920         
16921         if(q === undefined || q === null){
16922             q = '';
16923         }
16924         var qe = {
16925             query: q,
16926             forceAll: forceAll,
16927             combo: this,
16928             cancel:false
16929         };
16930         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16931             return false;
16932         }
16933         q = qe.query;
16934         
16935         forceAll = qe.forceAll;
16936         if(forceAll === true || (q.length >= this.minChars)){
16937             
16938             this.hasQuery = true;
16939             
16940             if(this.lastQuery != q || this.alwaysQuery){
16941                 this.lastQuery = q;
16942                 if(this.mode == 'local'){
16943                     this.selectedIndex = -1;
16944                     if(forceAll){
16945                         this.store.clearFilter();
16946                     }else{
16947                         
16948                         if(this.specialFilter){
16949                             this.fireEvent('specialfilter', this);
16950                             this.onLoad();
16951                             return;
16952                         }
16953                         
16954                         this.store.filter(this.displayField, q);
16955                     }
16956                     
16957                     this.store.fireEvent("datachanged", this.store);
16958                     
16959                     this.onLoad();
16960                     
16961                     
16962                 }else{
16963                     
16964                     this.store.baseParams[this.queryParam] = q;
16965                     
16966                     var options = {params : this.getParams(q)};
16967                     
16968                     if(this.loadNext){
16969                         options.add = true;
16970                         options.params.start = this.page * this.pageSize;
16971                     }
16972                     
16973                     this.store.load(options);
16974                     
16975                     /*
16976                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16977                      *  we should expand the list on onLoad
16978                      *  so command out it
16979                      */
16980 //                    this.expand();
16981                 }
16982             }else{
16983                 this.selectedIndex = -1;
16984                 this.onLoad();   
16985             }
16986         }
16987         
16988         this.loadNext = false;
16989     },
16990     
16991     // private
16992     getParams : function(q){
16993         var p = {};
16994         //p[this.queryParam] = q;
16995         
16996         if(this.pageSize){
16997             p.start = 0;
16998             p.limit = this.pageSize;
16999         }
17000         return p;
17001     },
17002
17003     /**
17004      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17005      */
17006     collapse : function(){
17007         if(!this.isExpanded()){
17008             return;
17009         }
17010         
17011         this.list.hide();
17012         
17013         this.hasFocus = false;
17014         
17015         if(this.tickable){
17016             this.okBtn.hide();
17017             this.cancelBtn.hide();
17018             this.trigger.show();
17019             
17020             if(this.editable){
17021                 this.tickableInputEl().dom.value = '';
17022                 this.tickableInputEl().blur();
17023             }
17024             
17025         }
17026         
17027         Roo.get(document).un('mousedown', this.collapseIf, this);
17028         Roo.get(document).un('mousewheel', this.collapseIf, this);
17029         if (!this.editable) {
17030             Roo.get(document).un('keydown', this.listKeyPress, this);
17031         }
17032         this.fireEvent('collapse', this);
17033         
17034         this.validate();
17035     },
17036
17037     // private
17038     collapseIf : function(e){
17039         var in_combo  = e.within(this.el);
17040         var in_list =  e.within(this.list);
17041         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17042         
17043         if (in_combo || in_list || is_list) {
17044             //e.stopPropagation();
17045             return;
17046         }
17047         
17048         if(this.tickable){
17049             this.onTickableFooterButtonClick(e, false, false);
17050         }
17051
17052         this.collapse();
17053         
17054     },
17055
17056     /**
17057      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17058      */
17059     expand : function(){
17060        
17061         if(this.isExpanded() || !this.hasFocus){
17062             return;
17063         }
17064         
17065         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17066         this.list.setWidth(lw);
17067         
17068         Roo.log('expand');
17069         
17070         this.list.show();
17071         
17072         this.restrictHeight();
17073         
17074         if(this.tickable){
17075             
17076             this.tickItems = Roo.apply([], this.item);
17077             
17078             this.okBtn.show();
17079             this.cancelBtn.show();
17080             this.trigger.hide();
17081             
17082             if(this.editable){
17083                 this.tickableInputEl().focus();
17084             }
17085             
17086         }
17087         
17088         Roo.get(document).on('mousedown', this.collapseIf, this);
17089         Roo.get(document).on('mousewheel', this.collapseIf, this);
17090         if (!this.editable) {
17091             Roo.get(document).on('keydown', this.listKeyPress, this);
17092         }
17093         
17094         this.fireEvent('expand', this);
17095     },
17096
17097     // private
17098     // Implements the default empty TriggerField.onTriggerClick function
17099     onTriggerClick : function(e)
17100     {
17101         Roo.log('trigger click');
17102         
17103         if(this.disabled || !this.triggerList){
17104             return;
17105         }
17106         
17107         this.page = 0;
17108         this.loadNext = false;
17109         
17110         if(this.isExpanded()){
17111             this.collapse();
17112             if (!this.blockFocus) {
17113                 this.inputEl().focus();
17114             }
17115             
17116         }else {
17117             this.hasFocus = true;
17118             if(this.triggerAction == 'all') {
17119                 this.doQuery(this.allQuery, true);
17120             } else {
17121                 this.doQuery(this.getRawValue());
17122             }
17123             if (!this.blockFocus) {
17124                 this.inputEl().focus();
17125             }
17126         }
17127     },
17128     
17129     onTickableTriggerClick : function(e)
17130     {
17131         if(this.disabled){
17132             return;
17133         }
17134         
17135         this.page = 0;
17136         this.loadNext = false;
17137         this.hasFocus = true;
17138         
17139         if(this.triggerAction == 'all') {
17140             this.doQuery(this.allQuery, true);
17141         } else {
17142             this.doQuery(this.getRawValue());
17143         }
17144     },
17145     
17146     onSearchFieldClick : function(e)
17147     {
17148         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17149             this.onTickableFooterButtonClick(e, false, false);
17150             return;
17151         }
17152         
17153         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17154             return;
17155         }
17156         
17157         this.page = 0;
17158         this.loadNext = false;
17159         this.hasFocus = true;
17160         
17161         if(this.triggerAction == 'all') {
17162             this.doQuery(this.allQuery, true);
17163         } else {
17164             this.doQuery(this.getRawValue());
17165         }
17166     },
17167     
17168     listKeyPress : function(e)
17169     {
17170         //Roo.log('listkeypress');
17171         // scroll to first matching element based on key pres..
17172         if (e.isSpecialKey()) {
17173             return false;
17174         }
17175         var k = String.fromCharCode(e.getKey()).toUpperCase();
17176         //Roo.log(k);
17177         var match  = false;
17178         var csel = this.view.getSelectedNodes();
17179         var cselitem = false;
17180         if (csel.length) {
17181             var ix = this.view.indexOf(csel[0]);
17182             cselitem  = this.store.getAt(ix);
17183             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17184                 cselitem = false;
17185             }
17186             
17187         }
17188         
17189         this.store.each(function(v) { 
17190             if (cselitem) {
17191                 // start at existing selection.
17192                 if (cselitem.id == v.id) {
17193                     cselitem = false;
17194                 }
17195                 return true;
17196             }
17197                 
17198             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17199                 match = this.store.indexOf(v);
17200                 return false;
17201             }
17202             return true;
17203         }, this);
17204         
17205         if (match === false) {
17206             return true; // no more action?
17207         }
17208         // scroll to?
17209         this.view.select(match);
17210         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17211         sn.scrollIntoView(sn.dom.parentNode, false);
17212     },
17213     
17214     onViewScroll : function(e, t){
17215         
17216         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){
17217             return;
17218         }
17219         
17220         this.hasQuery = true;
17221         
17222         this.loading = this.list.select('.loading', true).first();
17223         
17224         if(this.loading === null){
17225             this.list.createChild({
17226                 tag: 'div',
17227                 cls: 'loading roo-select2-more-results roo-select2-active',
17228                 html: 'Loading more results...'
17229             });
17230             
17231             this.loading = this.list.select('.loading', true).first();
17232             
17233             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17234             
17235             this.loading.hide();
17236         }
17237         
17238         this.loading.show();
17239         
17240         var _combo = this;
17241         
17242         this.page++;
17243         this.loadNext = true;
17244         
17245         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17246         
17247         return;
17248     },
17249     
17250     addItem : function(o)
17251     {   
17252         var dv = ''; // display value
17253         
17254         if (this.displayField) {
17255             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17256         } else {
17257             // this is an error condition!!!
17258             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17259         }
17260         
17261         if(!dv.length){
17262             return;
17263         }
17264         
17265         var choice = this.choices.createChild({
17266             tag: 'li',
17267             cls: 'roo-select2-search-choice',
17268             cn: [
17269                 {
17270                     tag: 'div',
17271                     html: dv
17272                 },
17273                 {
17274                     tag: 'a',
17275                     href: '#',
17276                     cls: 'roo-select2-search-choice-close fa fa-times',
17277                     tabindex: '-1'
17278                 }
17279             ]
17280             
17281         }, this.searchField);
17282         
17283         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17284         
17285         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17286         
17287         this.item.push(o);
17288         
17289         this.lastData = o;
17290         
17291         this.syncValue();
17292         
17293         this.inputEl().dom.value = '';
17294         
17295         this.validate();
17296     },
17297     
17298     onRemoveItem : function(e, _self, o)
17299     {
17300         e.preventDefault();
17301         
17302         this.lastItem = Roo.apply([], this.item);
17303         
17304         var index = this.item.indexOf(o.data) * 1;
17305         
17306         if( index < 0){
17307             Roo.log('not this item?!');
17308             return;
17309         }
17310         
17311         this.item.splice(index, 1);
17312         o.item.remove();
17313         
17314         this.syncValue();
17315         
17316         this.fireEvent('remove', this, e);
17317         
17318         this.validate();
17319         
17320     },
17321     
17322     syncValue : function()
17323     {
17324         if(!this.item.length){
17325             this.clearValue();
17326             return;
17327         }
17328             
17329         var value = [];
17330         var _this = this;
17331         Roo.each(this.item, function(i){
17332             if(_this.valueField){
17333                 value.push(i[_this.valueField]);
17334                 return;
17335             }
17336
17337             value.push(i);
17338         });
17339
17340         this.value = value.join(',');
17341
17342         if(this.hiddenField){
17343             this.hiddenField.dom.value = this.value;
17344         }
17345         
17346         this.store.fireEvent("datachanged", this.store);
17347         
17348         this.validate();
17349     },
17350     
17351     clearItem : function()
17352     {
17353         if(!this.multiple){
17354             return;
17355         }
17356         
17357         this.item = [];
17358         
17359         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17360            c.remove();
17361         });
17362         
17363         this.syncValue();
17364         
17365         this.validate();
17366         
17367         if(this.tickable && !Roo.isTouch){
17368             this.view.refresh();
17369         }
17370     },
17371     
17372     inputEl: function ()
17373     {
17374         if(Roo.isIOS && this.useNativeIOS){
17375             return this.el.select('select.roo-ios-select', true).first();
17376         }
17377         
17378         if(Roo.isTouch && this.mobileTouchView){
17379             return this.el.select('input.form-control',true).first();
17380         }
17381         
17382         if(this.tickable){
17383             return this.searchField;
17384         }
17385         
17386         return this.el.select('input.form-control',true).first();
17387     },
17388     
17389     onTickableFooterButtonClick : function(e, btn, el)
17390     {
17391         e.preventDefault();
17392         
17393         this.lastItem = Roo.apply([], this.item);
17394         
17395         if(btn && btn.name == 'cancel'){
17396             this.tickItems = Roo.apply([], this.item);
17397             this.collapse();
17398             return;
17399         }
17400         
17401         this.clearItem();
17402         
17403         var _this = this;
17404         
17405         Roo.each(this.tickItems, function(o){
17406             _this.addItem(o);
17407         });
17408         
17409         this.collapse();
17410         
17411     },
17412     
17413     validate : function()
17414     {
17415         if(this.getVisibilityEl().hasClass('hidden')){
17416             return true;
17417         }
17418         
17419         var v = this.getRawValue();
17420         
17421         if(this.multiple){
17422             v = this.getValue();
17423         }
17424         
17425         if(this.disabled || this.allowBlank || v.length){
17426             this.markValid();
17427             return true;
17428         }
17429         
17430         this.markInvalid();
17431         return false;
17432     },
17433     
17434     tickableInputEl : function()
17435     {
17436         if(!this.tickable || !this.editable){
17437             return this.inputEl();
17438         }
17439         
17440         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17441     },
17442     
17443     
17444     getAutoCreateTouchView : function()
17445     {
17446         var id = Roo.id();
17447         
17448         var cfg = {
17449             cls: 'form-group' //input-group
17450         };
17451         
17452         var input =  {
17453             tag: 'input',
17454             id : id,
17455             type : this.inputType,
17456             cls : 'form-control x-combo-noedit',
17457             autocomplete: 'new-password',
17458             placeholder : this.placeholder || '',
17459             readonly : true
17460         };
17461         
17462         if (this.name) {
17463             input.name = this.name;
17464         }
17465         
17466         if (this.size) {
17467             input.cls += ' input-' + this.size;
17468         }
17469         
17470         if (this.disabled) {
17471             input.disabled = true;
17472         }
17473         
17474         var inputblock = {
17475             cls : 'roo-combobox-wrap',
17476             cn : [
17477                 input
17478             ]
17479         };
17480         
17481         if(this.before){
17482             inputblock.cls += ' input-group';
17483             
17484             inputblock.cn.unshift({
17485                 tag :'span',
17486                 cls : 'input-group-addon input-group-prepend input-group-text',
17487                 html : this.before
17488             });
17489         }
17490         
17491         if(this.removable && !this.multiple){
17492             inputblock.cls += ' roo-removable';
17493             
17494             inputblock.cn.push({
17495                 tag: 'button',
17496                 html : 'x',
17497                 cls : 'roo-combo-removable-btn close'
17498             });
17499         }
17500
17501         if(this.hasFeedback && !this.allowBlank){
17502             
17503             inputblock.cls += ' has-feedback';
17504             
17505             inputblock.cn.push({
17506                 tag: 'span',
17507                 cls: 'glyphicon form-control-feedback'
17508             });
17509             
17510         }
17511         
17512         if (this.after) {
17513             
17514             inputblock.cls += (this.before) ? '' : ' input-group';
17515             
17516             inputblock.cn.push({
17517                 tag :'span',
17518                 cls : 'input-group-addon input-group-append input-group-text',
17519                 html : this.after
17520             });
17521         }
17522
17523         
17524         var ibwrap = inputblock;
17525         
17526         if(this.multiple){
17527             ibwrap = {
17528                 tag: 'ul',
17529                 cls: 'roo-select2-choices',
17530                 cn:[
17531                     {
17532                         tag: 'li',
17533                         cls: 'roo-select2-search-field',
17534                         cn: [
17535
17536                             inputblock
17537                         ]
17538                     }
17539                 ]
17540             };
17541         
17542             
17543         }
17544         
17545         var combobox = {
17546             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17547             cn: [
17548                 {
17549                     tag: 'input',
17550                     type : 'hidden',
17551                     cls: 'form-hidden-field'
17552                 },
17553                 ibwrap
17554             ]
17555         };
17556         
17557         if(!this.multiple && this.showToggleBtn){
17558             
17559             var caret = {
17560                 cls: 'caret'
17561             };
17562             
17563             if (this.caret != false) {
17564                 caret = {
17565                      tag: 'i',
17566                      cls: 'fa fa-' + this.caret
17567                 };
17568                 
17569             }
17570             
17571             combobox.cn.push({
17572                 tag :'span',
17573                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17574                 cn : [
17575                     Roo.bootstrap.version == 3 ? caret : '',
17576                     {
17577                         tag: 'span',
17578                         cls: 'combobox-clear',
17579                         cn  : [
17580                             {
17581                                 tag : 'i',
17582                                 cls: 'icon-remove'
17583                             }
17584                         ]
17585                     }
17586                 ]
17587
17588             })
17589         }
17590         
17591         if(this.multiple){
17592             combobox.cls += ' roo-select2-container-multi';
17593         }
17594         
17595         var required =  this.allowBlank ?  {
17596                     tag : 'i',
17597                     style: 'display: none'
17598                 } : {
17599                    tag : 'i',
17600                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17601                    tooltip : 'This field is required'
17602                 };
17603         
17604         var align = this.labelAlign || this.parentLabelAlign();
17605         
17606         if (align ==='left' && this.fieldLabel.length) {
17607
17608             cfg.cn = [
17609                 required,
17610                 {
17611                     tag: 'label',
17612                     cls : 'control-label col-form-label',
17613                     html : this.fieldLabel
17614
17615                 },
17616                 {
17617                     cls : 'roo-combobox-wrap ', 
17618                     cn: [
17619                         combobox
17620                     ]
17621                 }
17622             ];
17623             
17624             var labelCfg = cfg.cn[1];
17625             var contentCfg = cfg.cn[2];
17626             
17627
17628             if(this.indicatorpos == 'right'){
17629                 cfg.cn = [
17630                     {
17631                         tag: 'label',
17632                         'for' :  id,
17633                         cls : 'control-label col-form-label',
17634                         cn : [
17635                             {
17636                                 tag : 'span',
17637                                 html : this.fieldLabel
17638                             },
17639                             required
17640                         ]
17641                     },
17642                     {
17643                         cls : "roo-combobox-wrap ",
17644                         cn: [
17645                             combobox
17646                         ]
17647                     }
17648
17649                 ];
17650                 
17651                 labelCfg = cfg.cn[0];
17652                 contentCfg = cfg.cn[1];
17653             }
17654             
17655            
17656             
17657             if(this.labelWidth > 12){
17658                 labelCfg.style = "width: " + this.labelWidth + 'px';
17659             }
17660            
17661             if(this.labelWidth < 13 && this.labelmd == 0){
17662                 this.labelmd = this.labelWidth;
17663             }
17664             
17665             if(this.labellg > 0){
17666                 labelCfg.cls += ' col-lg-' + this.labellg;
17667                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17668             }
17669             
17670             if(this.labelmd > 0){
17671                 labelCfg.cls += ' col-md-' + this.labelmd;
17672                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17673             }
17674             
17675             if(this.labelsm > 0){
17676                 labelCfg.cls += ' col-sm-' + this.labelsm;
17677                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17678             }
17679             
17680             if(this.labelxs > 0){
17681                 labelCfg.cls += ' col-xs-' + this.labelxs;
17682                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17683             }
17684                 
17685                 
17686         } else if ( this.fieldLabel.length) {
17687             cfg.cn = [
17688                required,
17689                 {
17690                     tag: 'label',
17691                     cls : 'control-label',
17692                     html : this.fieldLabel
17693
17694                 },
17695                 {
17696                     cls : '', 
17697                     cn: [
17698                         combobox
17699                     ]
17700                 }
17701             ];
17702             
17703             if(this.indicatorpos == 'right'){
17704                 cfg.cn = [
17705                     {
17706                         tag: 'label',
17707                         cls : 'control-label',
17708                         html : this.fieldLabel,
17709                         cn : [
17710                             required
17711                         ]
17712                     },
17713                     {
17714                         cls : '', 
17715                         cn: [
17716                             combobox
17717                         ]
17718                     }
17719                 ];
17720             }
17721         } else {
17722             cfg.cn = combobox;    
17723         }
17724         
17725         
17726         var settings = this;
17727         
17728         ['xs','sm','md','lg'].map(function(size){
17729             if (settings[size]) {
17730                 cfg.cls += ' col-' + size + '-' + settings[size];
17731             }
17732         });
17733         
17734         return cfg;
17735     },
17736     
17737     initTouchView : function()
17738     {
17739         this.renderTouchView();
17740         
17741         this.touchViewEl.on('scroll', function(){
17742             this.el.dom.scrollTop = 0;
17743         }, this);
17744         
17745         this.originalValue = this.getValue();
17746         
17747         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17748         
17749         this.inputEl().on("click", this.showTouchView, this);
17750         if (this.triggerEl) {
17751             this.triggerEl.on("click", this.showTouchView, this);
17752         }
17753         
17754         
17755         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17756         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17757         
17758         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17759         
17760         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17761         this.store.on('load', this.onTouchViewLoad, this);
17762         this.store.on('loadexception', this.onTouchViewLoadException, this);
17763         
17764         if(this.hiddenName){
17765             
17766             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17767             
17768             this.hiddenField.dom.value =
17769                 this.hiddenValue !== undefined ? this.hiddenValue :
17770                 this.value !== undefined ? this.value : '';
17771         
17772             this.el.dom.removeAttribute('name');
17773             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17774         }
17775         
17776         if(this.multiple){
17777             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17778             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17779         }
17780         
17781         if(this.removable && !this.multiple){
17782             var close = this.closeTriggerEl();
17783             if(close){
17784                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17785                 close.on('click', this.removeBtnClick, this, close);
17786             }
17787         }
17788         /*
17789          * fix the bug in Safari iOS8
17790          */
17791         this.inputEl().on("focus", function(e){
17792             document.activeElement.blur();
17793         }, this);
17794         
17795         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17796         
17797         return;
17798         
17799         
17800     },
17801     
17802     renderTouchView : function()
17803     {
17804         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17805         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17806         
17807         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17808         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17809         
17810         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17811         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17812         this.touchViewBodyEl.setStyle('overflow', 'auto');
17813         
17814         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17815         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17816         
17817         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17818         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17819         
17820     },
17821     
17822     showTouchView : function()
17823     {
17824         if(this.disabled){
17825             return;
17826         }
17827         
17828         this.touchViewHeaderEl.hide();
17829
17830         if(this.modalTitle.length){
17831             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17832             this.touchViewHeaderEl.show();
17833         }
17834
17835         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17836         this.touchViewEl.show();
17837
17838         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17839         
17840         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17841         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17842
17843         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17844
17845         if(this.modalTitle.length){
17846             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17847         }
17848         
17849         this.touchViewBodyEl.setHeight(bodyHeight);
17850
17851         if(this.animate){
17852             var _this = this;
17853             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17854         }else{
17855             this.touchViewEl.addClass(['in','show']);
17856         }
17857         
17858         if(this._touchViewMask){
17859             Roo.get(document.body).addClass("x-body-masked");
17860             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17861             this._touchViewMask.setStyle('z-index', 10000);
17862             this._touchViewMask.addClass('show');
17863         }
17864         
17865         this.doTouchViewQuery();
17866         
17867     },
17868     
17869     hideTouchView : function()
17870     {
17871         this.touchViewEl.removeClass(['in','show']);
17872
17873         if(this.animate){
17874             var _this = this;
17875             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17876         }else{
17877             this.touchViewEl.setStyle('display', 'none');
17878         }
17879         
17880         if(this._touchViewMask){
17881             this._touchViewMask.removeClass('show');
17882             Roo.get(document.body).removeClass("x-body-masked");
17883         }
17884     },
17885     
17886     setTouchViewValue : function()
17887     {
17888         if(this.multiple){
17889             this.clearItem();
17890         
17891             var _this = this;
17892
17893             Roo.each(this.tickItems, function(o){
17894                 this.addItem(o);
17895             }, this);
17896         }
17897         
17898         this.hideTouchView();
17899     },
17900     
17901     doTouchViewQuery : function()
17902     {
17903         var qe = {
17904             query: '',
17905             forceAll: true,
17906             combo: this,
17907             cancel:false
17908         };
17909         
17910         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17911             return false;
17912         }
17913         
17914         if(!this.alwaysQuery || this.mode == 'local'){
17915             this.onTouchViewLoad();
17916             return;
17917         }
17918         
17919         this.store.load();
17920     },
17921     
17922     onTouchViewBeforeLoad : function(combo,opts)
17923     {
17924         return;
17925     },
17926
17927     // private
17928     onTouchViewLoad : function()
17929     {
17930         if(this.store.getCount() < 1){
17931             this.onTouchViewEmptyResults();
17932             return;
17933         }
17934         
17935         this.clearTouchView();
17936         
17937         var rawValue = this.getRawValue();
17938         
17939         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17940         
17941         this.tickItems = [];
17942         
17943         this.store.data.each(function(d, rowIndex){
17944             var row = this.touchViewListGroup.createChild(template);
17945             
17946             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17947                 row.addClass(d.data.cls);
17948             }
17949             
17950             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17951                 var cfg = {
17952                     data : d.data,
17953                     html : d.data[this.displayField]
17954                 };
17955                 
17956                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17957                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17958                 }
17959             }
17960             row.removeClass('selected');
17961             if(!this.multiple && this.valueField &&
17962                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17963             {
17964                 // radio buttons..
17965                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17966                 row.addClass('selected');
17967             }
17968             
17969             if(this.multiple && this.valueField &&
17970                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17971             {
17972                 
17973                 // checkboxes...
17974                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17975                 this.tickItems.push(d.data);
17976             }
17977             
17978             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17979             
17980         }, this);
17981         
17982         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17983         
17984         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17985
17986         if(this.modalTitle.length){
17987             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17988         }
17989
17990         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17991         
17992         if(this.mobile_restrict_height && listHeight < bodyHeight){
17993             this.touchViewBodyEl.setHeight(listHeight);
17994         }
17995         
17996         var _this = this;
17997         
17998         if(firstChecked && listHeight > bodyHeight){
17999             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18000         }
18001         
18002     },
18003     
18004     onTouchViewLoadException : function()
18005     {
18006         this.hideTouchView();
18007     },
18008     
18009     onTouchViewEmptyResults : function()
18010     {
18011         this.clearTouchView();
18012         
18013         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18014         
18015         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18016         
18017     },
18018     
18019     clearTouchView : function()
18020     {
18021         this.touchViewListGroup.dom.innerHTML = '';
18022     },
18023     
18024     onTouchViewClick : function(e, el, o)
18025     {
18026         e.preventDefault();
18027         
18028         var row = o.row;
18029         var rowIndex = o.rowIndex;
18030         
18031         var r = this.store.getAt(rowIndex);
18032         
18033         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18034             
18035             if(!this.multiple){
18036                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18037                     c.dom.removeAttribute('checked');
18038                 }, this);
18039
18040                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18041
18042                 this.setFromData(r.data);
18043
18044                 var close = this.closeTriggerEl();
18045
18046                 if(close){
18047                     close.show();
18048                 }
18049
18050                 this.hideTouchView();
18051
18052                 this.fireEvent('select', this, r, rowIndex);
18053
18054                 return;
18055             }
18056
18057             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18058                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18059                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18060                 return;
18061             }
18062
18063             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18064             this.addItem(r.data);
18065             this.tickItems.push(r.data);
18066         }
18067     },
18068     
18069     getAutoCreateNativeIOS : function()
18070     {
18071         var cfg = {
18072             cls: 'form-group' //input-group,
18073         };
18074         
18075         var combobox =  {
18076             tag: 'select',
18077             cls : 'roo-ios-select'
18078         };
18079         
18080         if (this.name) {
18081             combobox.name = this.name;
18082         }
18083         
18084         if (this.disabled) {
18085             combobox.disabled = true;
18086         }
18087         
18088         var settings = this;
18089         
18090         ['xs','sm','md','lg'].map(function(size){
18091             if (settings[size]) {
18092                 cfg.cls += ' col-' + size + '-' + settings[size];
18093             }
18094         });
18095         
18096         cfg.cn = combobox;
18097         
18098         return cfg;
18099         
18100     },
18101     
18102     initIOSView : function()
18103     {
18104         this.store.on('load', this.onIOSViewLoad, this);
18105         
18106         return;
18107     },
18108     
18109     onIOSViewLoad : function()
18110     {
18111         if(this.store.getCount() < 1){
18112             return;
18113         }
18114         
18115         this.clearIOSView();
18116         
18117         if(this.allowBlank) {
18118             
18119             var default_text = '-- SELECT --';
18120             
18121             if(this.placeholder.length){
18122                 default_text = this.placeholder;
18123             }
18124             
18125             if(this.emptyTitle.length){
18126                 default_text += ' - ' + this.emptyTitle + ' -';
18127             }
18128             
18129             var opt = this.inputEl().createChild({
18130                 tag: 'option',
18131                 value : 0,
18132                 html : default_text
18133             });
18134             
18135             var o = {};
18136             o[this.valueField] = 0;
18137             o[this.displayField] = default_text;
18138             
18139             this.ios_options.push({
18140                 data : o,
18141                 el : opt
18142             });
18143             
18144         }
18145         
18146         this.store.data.each(function(d, rowIndex){
18147             
18148             var html = '';
18149             
18150             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18151                 html = d.data[this.displayField];
18152             }
18153             
18154             var value = '';
18155             
18156             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18157                 value = d.data[this.valueField];
18158             }
18159             
18160             var option = {
18161                 tag: 'option',
18162                 value : value,
18163                 html : html
18164             };
18165             
18166             if(this.value == d.data[this.valueField]){
18167                 option['selected'] = true;
18168             }
18169             
18170             var opt = this.inputEl().createChild(option);
18171             
18172             this.ios_options.push({
18173                 data : d.data,
18174                 el : opt
18175             });
18176             
18177         }, this);
18178         
18179         this.inputEl().on('change', function(){
18180            this.fireEvent('select', this);
18181         }, this);
18182         
18183     },
18184     
18185     clearIOSView: function()
18186     {
18187         this.inputEl().dom.innerHTML = '';
18188         
18189         this.ios_options = [];
18190     },
18191     
18192     setIOSValue: function(v)
18193     {
18194         this.value = v;
18195         
18196         if(!this.ios_options){
18197             return;
18198         }
18199         
18200         Roo.each(this.ios_options, function(opts){
18201            
18202            opts.el.dom.removeAttribute('selected');
18203            
18204            if(opts.data[this.valueField] != v){
18205                return;
18206            }
18207            
18208            opts.el.dom.setAttribute('selected', true);
18209            
18210         }, this);
18211     }
18212
18213     /** 
18214     * @cfg {Boolean} grow 
18215     * @hide 
18216     */
18217     /** 
18218     * @cfg {Number} growMin 
18219     * @hide 
18220     */
18221     /** 
18222     * @cfg {Number} growMax 
18223     * @hide 
18224     */
18225     /**
18226      * @hide
18227      * @method autoSize
18228      */
18229 });
18230
18231 Roo.apply(Roo.bootstrap.ComboBox,  {
18232     
18233     header : {
18234         tag: 'div',
18235         cls: 'modal-header',
18236         cn: [
18237             {
18238                 tag: 'h4',
18239                 cls: 'modal-title'
18240             }
18241         ]
18242     },
18243     
18244     body : {
18245         tag: 'div',
18246         cls: 'modal-body',
18247         cn: [
18248             {
18249                 tag: 'ul',
18250                 cls: 'list-group'
18251             }
18252         ]
18253     },
18254     
18255     listItemRadio : {
18256         tag: 'li',
18257         cls: 'list-group-item',
18258         cn: [
18259             {
18260                 tag: 'span',
18261                 cls: 'roo-combobox-list-group-item-value'
18262             },
18263             {
18264                 tag: 'div',
18265                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18266                 cn: [
18267                     {
18268                         tag: 'input',
18269                         type: 'radio'
18270                     },
18271                     {
18272                         tag: 'label'
18273                     }
18274                 ]
18275             }
18276         ]
18277     },
18278     
18279     listItemCheckbox : {
18280         tag: 'li',
18281         cls: 'list-group-item',
18282         cn: [
18283             {
18284                 tag: 'span',
18285                 cls: 'roo-combobox-list-group-item-value'
18286             },
18287             {
18288                 tag: 'div',
18289                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18290                 cn: [
18291                     {
18292                         tag: 'input',
18293                         type: 'checkbox'
18294                     },
18295                     {
18296                         tag: 'label'
18297                     }
18298                 ]
18299             }
18300         ]
18301     },
18302     
18303     emptyResult : {
18304         tag: 'div',
18305         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18306     },
18307     
18308     footer : {
18309         tag: 'div',
18310         cls: 'modal-footer',
18311         cn: [
18312             {
18313                 tag: 'div',
18314                 cls: 'row',
18315                 cn: [
18316                     {
18317                         tag: 'div',
18318                         cls: 'col-xs-6 text-left',
18319                         cn: {
18320                             tag: 'button',
18321                             cls: 'btn btn-danger roo-touch-view-cancel',
18322                             html: 'Cancel'
18323                         }
18324                     },
18325                     {
18326                         tag: 'div',
18327                         cls: 'col-xs-6 text-right',
18328                         cn: {
18329                             tag: 'button',
18330                             cls: 'btn btn-success roo-touch-view-ok',
18331                             html: 'OK'
18332                         }
18333                     }
18334                 ]
18335             }
18336         ]
18337         
18338     }
18339 });
18340
18341 Roo.apply(Roo.bootstrap.ComboBox,  {
18342     
18343     touchViewTemplate : {
18344         tag: 'div',
18345         cls: 'modal fade roo-combobox-touch-view',
18346         cn: [
18347             {
18348                 tag: 'div',
18349                 cls: 'modal-dialog',
18350                 style : 'position:fixed', // we have to fix position....
18351                 cn: [
18352                     {
18353                         tag: 'div',
18354                         cls: 'modal-content',
18355                         cn: [
18356                             Roo.bootstrap.ComboBox.header,
18357                             Roo.bootstrap.ComboBox.body,
18358                             Roo.bootstrap.ComboBox.footer
18359                         ]
18360                     }
18361                 ]
18362             }
18363         ]
18364     }
18365 });/*
18366  * Based on:
18367  * Ext JS Library 1.1.1
18368  * Copyright(c) 2006-2007, Ext JS, LLC.
18369  *
18370  * Originally Released Under LGPL - original licence link has changed is not relivant.
18371  *
18372  * Fork - LGPL
18373  * <script type="text/javascript">
18374  */
18375
18376 /**
18377  * @class Roo.View
18378  * @extends Roo.util.Observable
18379  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18380  * This class also supports single and multi selection modes. <br>
18381  * Create a data model bound view:
18382  <pre><code>
18383  var store = new Roo.data.Store(...);
18384
18385  var view = new Roo.View({
18386     el : "my-element",
18387     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18388  
18389     singleSelect: true,
18390     selectedClass: "ydataview-selected",
18391     store: store
18392  });
18393
18394  // listen for node click?
18395  view.on("click", function(vw, index, node, e){
18396  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18397  });
18398
18399  // load XML data
18400  dataModel.load("foobar.xml");
18401  </code></pre>
18402  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18403  * <br><br>
18404  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18405  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18406  * 
18407  * Note: old style constructor is still suported (container, template, config)
18408  * 
18409  * @constructor
18410  * Create a new View
18411  * @param {Object} config The config object
18412  * 
18413  */
18414 Roo.View = function(config, depreciated_tpl, depreciated_config){
18415     
18416     this.parent = false;
18417     
18418     if (typeof(depreciated_tpl) == 'undefined') {
18419         // new way.. - universal constructor.
18420         Roo.apply(this, config);
18421         this.el  = Roo.get(this.el);
18422     } else {
18423         // old format..
18424         this.el  = Roo.get(config);
18425         this.tpl = depreciated_tpl;
18426         Roo.apply(this, depreciated_config);
18427     }
18428     this.wrapEl  = this.el.wrap().wrap();
18429     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18430     
18431     
18432     if(typeof(this.tpl) == "string"){
18433         this.tpl = new Roo.Template(this.tpl);
18434     } else {
18435         // support xtype ctors..
18436         this.tpl = new Roo.factory(this.tpl, Roo);
18437     }
18438     
18439     
18440     this.tpl.compile();
18441     
18442     /** @private */
18443     this.addEvents({
18444         /**
18445          * @event beforeclick
18446          * Fires before a click is processed. Returns false to cancel the default action.
18447          * @param {Roo.View} this
18448          * @param {Number} index The index of the target node
18449          * @param {HTMLElement} node The target node
18450          * @param {Roo.EventObject} e The raw event object
18451          */
18452             "beforeclick" : true,
18453         /**
18454          * @event click
18455          * Fires when a template node is clicked.
18456          * @param {Roo.View} this
18457          * @param {Number} index The index of the target node
18458          * @param {HTMLElement} node The target node
18459          * @param {Roo.EventObject} e The raw event object
18460          */
18461             "click" : true,
18462         /**
18463          * @event dblclick
18464          * Fires when a template node is double clicked.
18465          * @param {Roo.View} this
18466          * @param {Number} index The index of the target node
18467          * @param {HTMLElement} node The target node
18468          * @param {Roo.EventObject} e The raw event object
18469          */
18470             "dblclick" : true,
18471         /**
18472          * @event contextmenu
18473          * Fires when a template node is right clicked.
18474          * @param {Roo.View} this
18475          * @param {Number} index The index of the target node
18476          * @param {HTMLElement} node The target node
18477          * @param {Roo.EventObject} e The raw event object
18478          */
18479             "contextmenu" : true,
18480         /**
18481          * @event selectionchange
18482          * Fires when the selected nodes change.
18483          * @param {Roo.View} this
18484          * @param {Array} selections Array of the selected nodes
18485          */
18486             "selectionchange" : true,
18487     
18488         /**
18489          * @event beforeselect
18490          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18491          * @param {Roo.View} this
18492          * @param {HTMLElement} node The node to be selected
18493          * @param {Array} selections Array of currently selected nodes
18494          */
18495             "beforeselect" : true,
18496         /**
18497          * @event preparedata
18498          * Fires on every row to render, to allow you to change the data.
18499          * @param {Roo.View} this
18500          * @param {Object} data to be rendered (change this)
18501          */
18502           "preparedata" : true
18503           
18504           
18505         });
18506
18507
18508
18509     this.el.on({
18510         "click": this.onClick,
18511         "dblclick": this.onDblClick,
18512         "contextmenu": this.onContextMenu,
18513         scope:this
18514     });
18515
18516     this.selections = [];
18517     this.nodes = [];
18518     this.cmp = new Roo.CompositeElementLite([]);
18519     if(this.store){
18520         this.store = Roo.factory(this.store, Roo.data);
18521         this.setStore(this.store, true);
18522     }
18523     
18524     if ( this.footer && this.footer.xtype) {
18525            
18526          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18527         
18528         this.footer.dataSource = this.store;
18529         this.footer.container = fctr;
18530         this.footer = Roo.factory(this.footer, Roo);
18531         fctr.insertFirst(this.el);
18532         
18533         // this is a bit insane - as the paging toolbar seems to detach the el..
18534 //        dom.parentNode.parentNode.parentNode
18535          // they get detached?
18536     }
18537     
18538     
18539     Roo.View.superclass.constructor.call(this);
18540     
18541     
18542 };
18543
18544 Roo.extend(Roo.View, Roo.util.Observable, {
18545     
18546      /**
18547      * @cfg {Roo.data.Store} store Data store to load data from.
18548      */
18549     store : false,
18550     
18551     /**
18552      * @cfg {String|Roo.Element} el The container element.
18553      */
18554     el : '',
18555     
18556     /**
18557      * @cfg {String|Roo.Template} tpl The template used by this View 
18558      */
18559     tpl : false,
18560     /**
18561      * @cfg {String} dataName the named area of the template to use as the data area
18562      *                          Works with domtemplates roo-name="name"
18563      */
18564     dataName: false,
18565     /**
18566      * @cfg {String} selectedClass The css class to add to selected nodes
18567      */
18568     selectedClass : "x-view-selected",
18569      /**
18570      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18571      */
18572     emptyText : "",
18573     
18574     /**
18575      * @cfg {String} text to display on mask (default Loading)
18576      */
18577     mask : false,
18578     /**
18579      * @cfg {Boolean} multiSelect Allow multiple selection
18580      */
18581     multiSelect : false,
18582     /**
18583      * @cfg {Boolean} singleSelect Allow single selection
18584      */
18585     singleSelect:  false,
18586     
18587     /**
18588      * @cfg {Boolean} toggleSelect - selecting 
18589      */
18590     toggleSelect : false,
18591     
18592     /**
18593      * @cfg {Boolean} tickable - selecting 
18594      */
18595     tickable : false,
18596     
18597     /**
18598      * Returns the element this view is bound to.
18599      * @return {Roo.Element}
18600      */
18601     getEl : function(){
18602         return this.wrapEl;
18603     },
18604     
18605     
18606
18607     /**
18608      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18609      */
18610     refresh : function(){
18611         //Roo.log('refresh');
18612         var t = this.tpl;
18613         
18614         // if we are using something like 'domtemplate', then
18615         // the what gets used is:
18616         // t.applySubtemplate(NAME, data, wrapping data..)
18617         // the outer template then get' applied with
18618         //     the store 'extra data'
18619         // and the body get's added to the
18620         //      roo-name="data" node?
18621         //      <span class='roo-tpl-{name}'></span> ?????
18622         
18623         
18624         
18625         this.clearSelections();
18626         this.el.update("");
18627         var html = [];
18628         var records = this.store.getRange();
18629         if(records.length < 1) {
18630             
18631             // is this valid??  = should it render a template??
18632             
18633             this.el.update(this.emptyText);
18634             return;
18635         }
18636         var el = this.el;
18637         if (this.dataName) {
18638             this.el.update(t.apply(this.store.meta)); //????
18639             el = this.el.child('.roo-tpl-' + this.dataName);
18640         }
18641         
18642         for(var i = 0, len = records.length; i < len; i++){
18643             var data = this.prepareData(records[i].data, i, records[i]);
18644             this.fireEvent("preparedata", this, data, i, records[i]);
18645             
18646             var d = Roo.apply({}, data);
18647             
18648             if(this.tickable){
18649                 Roo.apply(d, {'roo-id' : Roo.id()});
18650                 
18651                 var _this = this;
18652             
18653                 Roo.each(this.parent.item, function(item){
18654                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18655                         return;
18656                     }
18657                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18658                 });
18659             }
18660             
18661             html[html.length] = Roo.util.Format.trim(
18662                 this.dataName ?
18663                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18664                     t.apply(d)
18665             );
18666         }
18667         
18668         
18669         
18670         el.update(html.join(""));
18671         this.nodes = el.dom.childNodes;
18672         this.updateIndexes(0);
18673     },
18674     
18675
18676     /**
18677      * Function to override to reformat the data that is sent to
18678      * the template for each node.
18679      * DEPRICATED - use the preparedata event handler.
18680      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18681      * a JSON object for an UpdateManager bound view).
18682      */
18683     prepareData : function(data, index, record)
18684     {
18685         this.fireEvent("preparedata", this, data, index, record);
18686         return data;
18687     },
18688
18689     onUpdate : function(ds, record){
18690         // Roo.log('on update');   
18691         this.clearSelections();
18692         var index = this.store.indexOf(record);
18693         var n = this.nodes[index];
18694         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18695         n.parentNode.removeChild(n);
18696         this.updateIndexes(index, index);
18697     },
18698
18699     
18700     
18701 // --------- FIXME     
18702     onAdd : function(ds, records, index)
18703     {
18704         //Roo.log(['on Add', ds, records, index] );        
18705         this.clearSelections();
18706         if(this.nodes.length == 0){
18707             this.refresh();
18708             return;
18709         }
18710         var n = this.nodes[index];
18711         for(var i = 0, len = records.length; i < len; i++){
18712             var d = this.prepareData(records[i].data, i, records[i]);
18713             if(n){
18714                 this.tpl.insertBefore(n, d);
18715             }else{
18716                 
18717                 this.tpl.append(this.el, d);
18718             }
18719         }
18720         this.updateIndexes(index);
18721     },
18722
18723     onRemove : function(ds, record, index){
18724        // Roo.log('onRemove');
18725         this.clearSelections();
18726         var el = this.dataName  ?
18727             this.el.child('.roo-tpl-' + this.dataName) :
18728             this.el; 
18729         
18730         el.dom.removeChild(this.nodes[index]);
18731         this.updateIndexes(index);
18732     },
18733
18734     /**
18735      * Refresh an individual node.
18736      * @param {Number} index
18737      */
18738     refreshNode : function(index){
18739         this.onUpdate(this.store, this.store.getAt(index));
18740     },
18741
18742     updateIndexes : function(startIndex, endIndex){
18743         var ns = this.nodes;
18744         startIndex = startIndex || 0;
18745         endIndex = endIndex || ns.length - 1;
18746         for(var i = startIndex; i <= endIndex; i++){
18747             ns[i].nodeIndex = i;
18748         }
18749     },
18750
18751     /**
18752      * Changes the data store this view uses and refresh the view.
18753      * @param {Store} store
18754      */
18755     setStore : function(store, initial){
18756         if(!initial && this.store){
18757             this.store.un("datachanged", this.refresh);
18758             this.store.un("add", this.onAdd);
18759             this.store.un("remove", this.onRemove);
18760             this.store.un("update", this.onUpdate);
18761             this.store.un("clear", this.refresh);
18762             this.store.un("beforeload", this.onBeforeLoad);
18763             this.store.un("load", this.onLoad);
18764             this.store.un("loadexception", this.onLoad);
18765         }
18766         if(store){
18767           
18768             store.on("datachanged", this.refresh, this);
18769             store.on("add", this.onAdd, this);
18770             store.on("remove", this.onRemove, this);
18771             store.on("update", this.onUpdate, this);
18772             store.on("clear", this.refresh, this);
18773             store.on("beforeload", this.onBeforeLoad, this);
18774             store.on("load", this.onLoad, this);
18775             store.on("loadexception", this.onLoad, this);
18776         }
18777         
18778         if(store){
18779             this.refresh();
18780         }
18781     },
18782     /**
18783      * onbeforeLoad - masks the loading area.
18784      *
18785      */
18786     onBeforeLoad : function(store,opts)
18787     {
18788          //Roo.log('onBeforeLoad');   
18789         if (!opts.add) {
18790             this.el.update("");
18791         }
18792         this.el.mask(this.mask ? this.mask : "Loading" ); 
18793     },
18794     onLoad : function ()
18795     {
18796         this.el.unmask();
18797     },
18798     
18799
18800     /**
18801      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18802      * @param {HTMLElement} node
18803      * @return {HTMLElement} The template node
18804      */
18805     findItemFromChild : function(node){
18806         var el = this.dataName  ?
18807             this.el.child('.roo-tpl-' + this.dataName,true) :
18808             this.el.dom; 
18809         
18810         if(!node || node.parentNode == el){
18811                     return node;
18812             }
18813             var p = node.parentNode;
18814             while(p && p != el){
18815             if(p.parentNode == el){
18816                 return p;
18817             }
18818             p = p.parentNode;
18819         }
18820             return null;
18821     },
18822
18823     /** @ignore */
18824     onClick : function(e){
18825         var item = this.findItemFromChild(e.getTarget());
18826         if(item){
18827             var index = this.indexOf(item);
18828             if(this.onItemClick(item, index, e) !== false){
18829                 this.fireEvent("click", this, index, item, e);
18830             }
18831         }else{
18832             this.clearSelections();
18833         }
18834     },
18835
18836     /** @ignore */
18837     onContextMenu : function(e){
18838         var item = this.findItemFromChild(e.getTarget());
18839         if(item){
18840             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18841         }
18842     },
18843
18844     /** @ignore */
18845     onDblClick : function(e){
18846         var item = this.findItemFromChild(e.getTarget());
18847         if(item){
18848             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18849         }
18850     },
18851
18852     onItemClick : function(item, index, e)
18853     {
18854         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18855             return false;
18856         }
18857         if (this.toggleSelect) {
18858             var m = this.isSelected(item) ? 'unselect' : 'select';
18859             //Roo.log(m);
18860             var _t = this;
18861             _t[m](item, true, false);
18862             return true;
18863         }
18864         if(this.multiSelect || this.singleSelect){
18865             if(this.multiSelect && e.shiftKey && this.lastSelection){
18866                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18867             }else{
18868                 this.select(item, this.multiSelect && e.ctrlKey);
18869                 this.lastSelection = item;
18870             }
18871             
18872             if(!this.tickable){
18873                 e.preventDefault();
18874             }
18875             
18876         }
18877         return true;
18878     },
18879
18880     /**
18881      * Get the number of selected nodes.
18882      * @return {Number}
18883      */
18884     getSelectionCount : function(){
18885         return this.selections.length;
18886     },
18887
18888     /**
18889      * Get the currently selected nodes.
18890      * @return {Array} An array of HTMLElements
18891      */
18892     getSelectedNodes : function(){
18893         return this.selections;
18894     },
18895
18896     /**
18897      * Get the indexes of the selected nodes.
18898      * @return {Array}
18899      */
18900     getSelectedIndexes : function(){
18901         var indexes = [], s = this.selections;
18902         for(var i = 0, len = s.length; i < len; i++){
18903             indexes.push(s[i].nodeIndex);
18904         }
18905         return indexes;
18906     },
18907
18908     /**
18909      * Clear all selections
18910      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18911      */
18912     clearSelections : function(suppressEvent){
18913         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18914             this.cmp.elements = this.selections;
18915             this.cmp.removeClass(this.selectedClass);
18916             this.selections = [];
18917             if(!suppressEvent){
18918                 this.fireEvent("selectionchange", this, this.selections);
18919             }
18920         }
18921     },
18922
18923     /**
18924      * Returns true if the passed node is selected
18925      * @param {HTMLElement/Number} node The node or node index
18926      * @return {Boolean}
18927      */
18928     isSelected : function(node){
18929         var s = this.selections;
18930         if(s.length < 1){
18931             return false;
18932         }
18933         node = this.getNode(node);
18934         return s.indexOf(node) !== -1;
18935     },
18936
18937     /**
18938      * Selects nodes.
18939      * @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
18940      * @param {Boolean} keepExisting (optional) true to keep existing selections
18941      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18942      */
18943     select : function(nodeInfo, keepExisting, suppressEvent){
18944         if(nodeInfo instanceof Array){
18945             if(!keepExisting){
18946                 this.clearSelections(true);
18947             }
18948             for(var i = 0, len = nodeInfo.length; i < len; i++){
18949                 this.select(nodeInfo[i], true, true);
18950             }
18951             return;
18952         } 
18953         var node = this.getNode(nodeInfo);
18954         if(!node || this.isSelected(node)){
18955             return; // already selected.
18956         }
18957         if(!keepExisting){
18958             this.clearSelections(true);
18959         }
18960         
18961         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18962             Roo.fly(node).addClass(this.selectedClass);
18963             this.selections.push(node);
18964             if(!suppressEvent){
18965                 this.fireEvent("selectionchange", this, this.selections);
18966             }
18967         }
18968         
18969         
18970     },
18971       /**
18972      * Unselects nodes.
18973      * @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
18974      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18975      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18976      */
18977     unselect : function(nodeInfo, keepExisting, suppressEvent)
18978     {
18979         if(nodeInfo instanceof Array){
18980             Roo.each(this.selections, function(s) {
18981                 this.unselect(s, nodeInfo);
18982             }, this);
18983             return;
18984         }
18985         var node = this.getNode(nodeInfo);
18986         if(!node || !this.isSelected(node)){
18987             //Roo.log("not selected");
18988             return; // not selected.
18989         }
18990         // fireevent???
18991         var ns = [];
18992         Roo.each(this.selections, function(s) {
18993             if (s == node ) {
18994                 Roo.fly(node).removeClass(this.selectedClass);
18995
18996                 return;
18997             }
18998             ns.push(s);
18999         },this);
19000         
19001         this.selections= ns;
19002         this.fireEvent("selectionchange", this, this.selections);
19003     },
19004
19005     /**
19006      * Gets a template node.
19007      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19008      * @return {HTMLElement} The node or null if it wasn't found
19009      */
19010     getNode : function(nodeInfo){
19011         if(typeof nodeInfo == "string"){
19012             return document.getElementById(nodeInfo);
19013         }else if(typeof nodeInfo == "number"){
19014             return this.nodes[nodeInfo];
19015         }
19016         return nodeInfo;
19017     },
19018
19019     /**
19020      * Gets a range template nodes.
19021      * @param {Number} startIndex
19022      * @param {Number} endIndex
19023      * @return {Array} An array of nodes
19024      */
19025     getNodes : function(start, end){
19026         var ns = this.nodes;
19027         start = start || 0;
19028         end = typeof end == "undefined" ? ns.length - 1 : end;
19029         var nodes = [];
19030         if(start <= end){
19031             for(var i = start; i <= end; i++){
19032                 nodes.push(ns[i]);
19033             }
19034         } else{
19035             for(var i = start; i >= end; i--){
19036                 nodes.push(ns[i]);
19037             }
19038         }
19039         return nodes;
19040     },
19041
19042     /**
19043      * Finds the index of the passed node
19044      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19045      * @return {Number} The index of the node or -1
19046      */
19047     indexOf : function(node){
19048         node = this.getNode(node);
19049         if(typeof node.nodeIndex == "number"){
19050             return node.nodeIndex;
19051         }
19052         var ns = this.nodes;
19053         for(var i = 0, len = ns.length; i < len; i++){
19054             if(ns[i] == node){
19055                 return i;
19056             }
19057         }
19058         return -1;
19059     }
19060 });
19061 /*
19062  * - LGPL
19063  *
19064  * based on jquery fullcalendar
19065  * 
19066  */
19067
19068 Roo.bootstrap = Roo.bootstrap || {};
19069 /**
19070  * @class Roo.bootstrap.Calendar
19071  * @extends Roo.bootstrap.Component
19072  * Bootstrap Calendar class
19073  * @cfg {Boolean} loadMask (true|false) default false
19074  * @cfg {Object} header generate the user specific header of the calendar, default false
19075
19076  * @constructor
19077  * Create a new Container
19078  * @param {Object} config The config object
19079  */
19080
19081
19082
19083 Roo.bootstrap.Calendar = function(config){
19084     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19085      this.addEvents({
19086         /**
19087              * @event select
19088              * Fires when a date is selected
19089              * @param {DatePicker} this
19090              * @param {Date} date The selected date
19091              */
19092         'select': true,
19093         /**
19094              * @event monthchange
19095              * Fires when the displayed month changes 
19096              * @param {DatePicker} this
19097              * @param {Date} date The selected month
19098              */
19099         'monthchange': true,
19100         /**
19101              * @event evententer
19102              * Fires when mouse over an event
19103              * @param {Calendar} this
19104              * @param {event} Event
19105              */
19106         'evententer': true,
19107         /**
19108              * @event eventleave
19109              * Fires when the mouse leaves an
19110              * @param {Calendar} this
19111              * @param {event}
19112              */
19113         'eventleave': true,
19114         /**
19115              * @event eventclick
19116              * Fires when the mouse click an
19117              * @param {Calendar} this
19118              * @param {event}
19119              */
19120         'eventclick': true
19121         
19122     });
19123
19124 };
19125
19126 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19127     
19128      /**
19129      * @cfg {Number} startDay
19130      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19131      */
19132     startDay : 0,
19133     
19134     loadMask : false,
19135     
19136     header : false,
19137       
19138     getAutoCreate : function(){
19139         
19140         
19141         var fc_button = function(name, corner, style, content ) {
19142             return Roo.apply({},{
19143                 tag : 'span',
19144                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19145                          (corner.length ?
19146                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19147                             ''
19148                         ),
19149                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19150                 unselectable: 'on'
19151             });
19152         };
19153         
19154         var header = {};
19155         
19156         if(!this.header){
19157             header = {
19158                 tag : 'table',
19159                 cls : 'fc-header',
19160                 style : 'width:100%',
19161                 cn : [
19162                     {
19163                         tag: 'tr',
19164                         cn : [
19165                             {
19166                                 tag : 'td',
19167                                 cls : 'fc-header-left',
19168                                 cn : [
19169                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19170                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19171                                     { tag: 'span', cls: 'fc-header-space' },
19172                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19173
19174
19175                                 ]
19176                             },
19177
19178                             {
19179                                 tag : 'td',
19180                                 cls : 'fc-header-center',
19181                                 cn : [
19182                                     {
19183                                         tag: 'span',
19184                                         cls: 'fc-header-title',
19185                                         cn : {
19186                                             tag: 'H2',
19187                                             html : 'month / year'
19188                                         }
19189                                     }
19190
19191                                 ]
19192                             },
19193                             {
19194                                 tag : 'td',
19195                                 cls : 'fc-header-right',
19196                                 cn : [
19197                               /*      fc_button('month', 'left', '', 'month' ),
19198                                     fc_button('week', '', '', 'week' ),
19199                                     fc_button('day', 'right', '', 'day' )
19200                                 */    
19201
19202                                 ]
19203                             }
19204
19205                         ]
19206                     }
19207                 ]
19208             };
19209         }
19210         
19211         header = this.header;
19212         
19213        
19214         var cal_heads = function() {
19215             var ret = [];
19216             // fixme - handle this.
19217             
19218             for (var i =0; i < Date.dayNames.length; i++) {
19219                 var d = Date.dayNames[i];
19220                 ret.push({
19221                     tag: 'th',
19222                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19223                     html : d.substring(0,3)
19224                 });
19225                 
19226             }
19227             ret[0].cls += ' fc-first';
19228             ret[6].cls += ' fc-last';
19229             return ret;
19230         };
19231         var cal_cell = function(n) {
19232             return  {
19233                 tag: 'td',
19234                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19235                 cn : [
19236                     {
19237                         cn : [
19238                             {
19239                                 cls: 'fc-day-number',
19240                                 html: 'D'
19241                             },
19242                             {
19243                                 cls: 'fc-day-content',
19244                              
19245                                 cn : [
19246                                      {
19247                                         style: 'position: relative;' // height: 17px;
19248                                     }
19249                                 ]
19250                             }
19251                             
19252                             
19253                         ]
19254                     }
19255                 ]
19256                 
19257             }
19258         };
19259         var cal_rows = function() {
19260             
19261             var ret = [];
19262             for (var r = 0; r < 6; r++) {
19263                 var row= {
19264                     tag : 'tr',
19265                     cls : 'fc-week',
19266                     cn : []
19267                 };
19268                 
19269                 for (var i =0; i < Date.dayNames.length; i++) {
19270                     var d = Date.dayNames[i];
19271                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19272
19273                 }
19274                 row.cn[0].cls+=' fc-first';
19275                 row.cn[0].cn[0].style = 'min-height:90px';
19276                 row.cn[6].cls+=' fc-last';
19277                 ret.push(row);
19278                 
19279             }
19280             ret[0].cls += ' fc-first';
19281             ret[4].cls += ' fc-prev-last';
19282             ret[5].cls += ' fc-last';
19283             return ret;
19284             
19285         };
19286         
19287         var cal_table = {
19288             tag: 'table',
19289             cls: 'fc-border-separate',
19290             style : 'width:100%',
19291             cellspacing  : 0,
19292             cn : [
19293                 { 
19294                     tag: 'thead',
19295                     cn : [
19296                         { 
19297                             tag: 'tr',
19298                             cls : 'fc-first fc-last',
19299                             cn : cal_heads()
19300                         }
19301                     ]
19302                 },
19303                 { 
19304                     tag: 'tbody',
19305                     cn : cal_rows()
19306                 }
19307                   
19308             ]
19309         };
19310          
19311          var cfg = {
19312             cls : 'fc fc-ltr',
19313             cn : [
19314                 header,
19315                 {
19316                     cls : 'fc-content',
19317                     style : "position: relative;",
19318                     cn : [
19319                         {
19320                             cls : 'fc-view fc-view-month fc-grid',
19321                             style : 'position: relative',
19322                             unselectable : 'on',
19323                             cn : [
19324                                 {
19325                                     cls : 'fc-event-container',
19326                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19327                                 },
19328                                 cal_table
19329                             ]
19330                         }
19331                     ]
19332     
19333                 }
19334            ] 
19335             
19336         };
19337         
19338          
19339         
19340         return cfg;
19341     },
19342     
19343     
19344     initEvents : function()
19345     {
19346         if(!this.store){
19347             throw "can not find store for calendar";
19348         }
19349         
19350         var mark = {
19351             tag: "div",
19352             cls:"x-dlg-mask",
19353             style: "text-align:center",
19354             cn: [
19355                 {
19356                     tag: "div",
19357                     style: "background-color:white;width:50%;margin:250 auto",
19358                     cn: [
19359                         {
19360                             tag: "img",
19361                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19362                         },
19363                         {
19364                             tag: "span",
19365                             html: "Loading"
19366                         }
19367                         
19368                     ]
19369                 }
19370             ]
19371         };
19372         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19373         
19374         var size = this.el.select('.fc-content', true).first().getSize();
19375         this.maskEl.setSize(size.width, size.height);
19376         this.maskEl.enableDisplayMode("block");
19377         if(!this.loadMask){
19378             this.maskEl.hide();
19379         }
19380         
19381         this.store = Roo.factory(this.store, Roo.data);
19382         this.store.on('load', this.onLoad, this);
19383         this.store.on('beforeload', this.onBeforeLoad, this);
19384         
19385         this.resize();
19386         
19387         this.cells = this.el.select('.fc-day',true);
19388         //Roo.log(this.cells);
19389         this.textNodes = this.el.query('.fc-day-number');
19390         this.cells.addClassOnOver('fc-state-hover');
19391         
19392         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19393         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19394         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19395         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19396         
19397         this.on('monthchange', this.onMonthChange, this);
19398         
19399         this.update(new Date().clearTime());
19400     },
19401     
19402     resize : function() {
19403         var sz  = this.el.getSize();
19404         
19405         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19406         this.el.select('.fc-day-content div',true).setHeight(34);
19407     },
19408     
19409     
19410     // private
19411     showPrevMonth : function(e){
19412         this.update(this.activeDate.add("mo", -1));
19413     },
19414     showToday : function(e){
19415         this.update(new Date().clearTime());
19416     },
19417     // private
19418     showNextMonth : function(e){
19419         this.update(this.activeDate.add("mo", 1));
19420     },
19421
19422     // private
19423     showPrevYear : function(){
19424         this.update(this.activeDate.add("y", -1));
19425     },
19426
19427     // private
19428     showNextYear : function(){
19429         this.update(this.activeDate.add("y", 1));
19430     },
19431
19432     
19433    // private
19434     update : function(date)
19435     {
19436         var vd = this.activeDate;
19437         this.activeDate = date;
19438 //        if(vd && this.el){
19439 //            var t = date.getTime();
19440 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19441 //                Roo.log('using add remove');
19442 //                
19443 //                this.fireEvent('monthchange', this, date);
19444 //                
19445 //                this.cells.removeClass("fc-state-highlight");
19446 //                this.cells.each(function(c){
19447 //                   if(c.dateValue == t){
19448 //                       c.addClass("fc-state-highlight");
19449 //                       setTimeout(function(){
19450 //                            try{c.dom.firstChild.focus();}catch(e){}
19451 //                       }, 50);
19452 //                       return false;
19453 //                   }
19454 //                   return true;
19455 //                });
19456 //                return;
19457 //            }
19458 //        }
19459         
19460         var days = date.getDaysInMonth();
19461         
19462         var firstOfMonth = date.getFirstDateOfMonth();
19463         var startingPos = firstOfMonth.getDay()-this.startDay;
19464         
19465         if(startingPos < this.startDay){
19466             startingPos += 7;
19467         }
19468         
19469         var pm = date.add(Date.MONTH, -1);
19470         var prevStart = pm.getDaysInMonth()-startingPos;
19471 //        
19472         this.cells = this.el.select('.fc-day',true);
19473         this.textNodes = this.el.query('.fc-day-number');
19474         this.cells.addClassOnOver('fc-state-hover');
19475         
19476         var cells = this.cells.elements;
19477         var textEls = this.textNodes;
19478         
19479         Roo.each(cells, function(cell){
19480             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19481         });
19482         
19483         days += startingPos;
19484
19485         // convert everything to numbers so it's fast
19486         var day = 86400000;
19487         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19488         //Roo.log(d);
19489         //Roo.log(pm);
19490         //Roo.log(prevStart);
19491         
19492         var today = new Date().clearTime().getTime();
19493         var sel = date.clearTime().getTime();
19494         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19495         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19496         var ddMatch = this.disabledDatesRE;
19497         var ddText = this.disabledDatesText;
19498         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19499         var ddaysText = this.disabledDaysText;
19500         var format = this.format;
19501         
19502         var setCellClass = function(cal, cell){
19503             cell.row = 0;
19504             cell.events = [];
19505             cell.more = [];
19506             //Roo.log('set Cell Class');
19507             cell.title = "";
19508             var t = d.getTime();
19509             
19510             //Roo.log(d);
19511             
19512             cell.dateValue = t;
19513             if(t == today){
19514                 cell.className += " fc-today";
19515                 cell.className += " fc-state-highlight";
19516                 cell.title = cal.todayText;
19517             }
19518             if(t == sel){
19519                 // disable highlight in other month..
19520                 //cell.className += " fc-state-highlight";
19521                 
19522             }
19523             // disabling
19524             if(t < min) {
19525                 cell.className = " fc-state-disabled";
19526                 cell.title = cal.minText;
19527                 return;
19528             }
19529             if(t > max) {
19530                 cell.className = " fc-state-disabled";
19531                 cell.title = cal.maxText;
19532                 return;
19533             }
19534             if(ddays){
19535                 if(ddays.indexOf(d.getDay()) != -1){
19536                     cell.title = ddaysText;
19537                     cell.className = " fc-state-disabled";
19538                 }
19539             }
19540             if(ddMatch && format){
19541                 var fvalue = d.dateFormat(format);
19542                 if(ddMatch.test(fvalue)){
19543                     cell.title = ddText.replace("%0", fvalue);
19544                     cell.className = " fc-state-disabled";
19545                 }
19546             }
19547             
19548             if (!cell.initialClassName) {
19549                 cell.initialClassName = cell.dom.className;
19550             }
19551             
19552             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19553         };
19554
19555         var i = 0;
19556         
19557         for(; i < startingPos; i++) {
19558             textEls[i].innerHTML = (++prevStart);
19559             d.setDate(d.getDate()+1);
19560             
19561             cells[i].className = "fc-past fc-other-month";
19562             setCellClass(this, cells[i]);
19563         }
19564         
19565         var intDay = 0;
19566         
19567         for(; i < days; i++){
19568             intDay = i - startingPos + 1;
19569             textEls[i].innerHTML = (intDay);
19570             d.setDate(d.getDate()+1);
19571             
19572             cells[i].className = ''; // "x-date-active";
19573             setCellClass(this, cells[i]);
19574         }
19575         var extraDays = 0;
19576         
19577         for(; i < 42; i++) {
19578             textEls[i].innerHTML = (++extraDays);
19579             d.setDate(d.getDate()+1);
19580             
19581             cells[i].className = "fc-future fc-other-month";
19582             setCellClass(this, cells[i]);
19583         }
19584         
19585         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19586         
19587         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19588         
19589         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19590         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19591         
19592         if(totalRows != 6){
19593             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19594             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19595         }
19596         
19597         this.fireEvent('monthchange', this, date);
19598         
19599         
19600         /*
19601         if(!this.internalRender){
19602             var main = this.el.dom.firstChild;
19603             var w = main.offsetWidth;
19604             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19605             Roo.fly(main).setWidth(w);
19606             this.internalRender = true;
19607             // opera does not respect the auto grow header center column
19608             // then, after it gets a width opera refuses to recalculate
19609             // without a second pass
19610             if(Roo.isOpera && !this.secondPass){
19611                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19612                 this.secondPass = true;
19613                 this.update.defer(10, this, [date]);
19614             }
19615         }
19616         */
19617         
19618     },
19619     
19620     findCell : function(dt) {
19621         dt = dt.clearTime().getTime();
19622         var ret = false;
19623         this.cells.each(function(c){
19624             //Roo.log("check " +c.dateValue + '?=' + dt);
19625             if(c.dateValue == dt){
19626                 ret = c;
19627                 return false;
19628             }
19629             return true;
19630         });
19631         
19632         return ret;
19633     },
19634     
19635     findCells : function(ev) {
19636         var s = ev.start.clone().clearTime().getTime();
19637        // Roo.log(s);
19638         var e= ev.end.clone().clearTime().getTime();
19639        // Roo.log(e);
19640         var ret = [];
19641         this.cells.each(function(c){
19642              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19643             
19644             if(c.dateValue > e){
19645                 return ;
19646             }
19647             if(c.dateValue < s){
19648                 return ;
19649             }
19650             ret.push(c);
19651         });
19652         
19653         return ret;    
19654     },
19655     
19656 //    findBestRow: function(cells)
19657 //    {
19658 //        var ret = 0;
19659 //        
19660 //        for (var i =0 ; i < cells.length;i++) {
19661 //            ret  = Math.max(cells[i].rows || 0,ret);
19662 //        }
19663 //        return ret;
19664 //        
19665 //    },
19666     
19667     
19668     addItem : function(ev)
19669     {
19670         // look for vertical location slot in
19671         var cells = this.findCells(ev);
19672         
19673 //        ev.row = this.findBestRow(cells);
19674         
19675         // work out the location.
19676         
19677         var crow = false;
19678         var rows = [];
19679         for(var i =0; i < cells.length; i++) {
19680             
19681             cells[i].row = cells[0].row;
19682             
19683             if(i == 0){
19684                 cells[i].row = cells[i].row + 1;
19685             }
19686             
19687             if (!crow) {
19688                 crow = {
19689                     start : cells[i],
19690                     end :  cells[i]
19691                 };
19692                 continue;
19693             }
19694             if (crow.start.getY() == cells[i].getY()) {
19695                 // on same row.
19696                 crow.end = cells[i];
19697                 continue;
19698             }
19699             // different row.
19700             rows.push(crow);
19701             crow = {
19702                 start: cells[i],
19703                 end : cells[i]
19704             };
19705             
19706         }
19707         
19708         rows.push(crow);
19709         ev.els = [];
19710         ev.rows = rows;
19711         ev.cells = cells;
19712         
19713         cells[0].events.push(ev);
19714         
19715         this.calevents.push(ev);
19716     },
19717     
19718     clearEvents: function() {
19719         
19720         if(!this.calevents){
19721             return;
19722         }
19723         
19724         Roo.each(this.cells.elements, function(c){
19725             c.row = 0;
19726             c.events = [];
19727             c.more = [];
19728         });
19729         
19730         Roo.each(this.calevents, function(e) {
19731             Roo.each(e.els, function(el) {
19732                 el.un('mouseenter' ,this.onEventEnter, this);
19733                 el.un('mouseleave' ,this.onEventLeave, this);
19734                 el.remove();
19735             },this);
19736         },this);
19737         
19738         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19739             e.remove();
19740         });
19741         
19742     },
19743     
19744     renderEvents: function()
19745     {   
19746         var _this = this;
19747         
19748         this.cells.each(function(c) {
19749             
19750             if(c.row < 5){
19751                 return;
19752             }
19753             
19754             var ev = c.events;
19755             
19756             var r = 4;
19757             if(c.row != c.events.length){
19758                 r = 4 - (4 - (c.row - c.events.length));
19759             }
19760             
19761             c.events = ev.slice(0, r);
19762             c.more = ev.slice(r);
19763             
19764             if(c.more.length && c.more.length == 1){
19765                 c.events.push(c.more.pop());
19766             }
19767             
19768             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19769             
19770         });
19771             
19772         this.cells.each(function(c) {
19773             
19774             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19775             
19776             
19777             for (var e = 0; e < c.events.length; e++){
19778                 var ev = c.events[e];
19779                 var rows = ev.rows;
19780                 
19781                 for(var i = 0; i < rows.length; i++) {
19782                 
19783                     // how many rows should it span..
19784
19785                     var  cfg = {
19786                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19787                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19788
19789                         unselectable : "on",
19790                         cn : [
19791                             {
19792                                 cls: 'fc-event-inner',
19793                                 cn : [
19794     //                                {
19795     //                                  tag:'span',
19796     //                                  cls: 'fc-event-time',
19797     //                                  html : cells.length > 1 ? '' : ev.time
19798     //                                },
19799                                     {
19800                                       tag:'span',
19801                                       cls: 'fc-event-title',
19802                                       html : String.format('{0}', ev.title)
19803                                     }
19804
19805
19806                                 ]
19807                             },
19808                             {
19809                                 cls: 'ui-resizable-handle ui-resizable-e',
19810                                 html : '&nbsp;&nbsp;&nbsp'
19811                             }
19812
19813                         ]
19814                     };
19815
19816                     if (i == 0) {
19817                         cfg.cls += ' fc-event-start';
19818                     }
19819                     if ((i+1) == rows.length) {
19820                         cfg.cls += ' fc-event-end';
19821                     }
19822
19823                     var ctr = _this.el.select('.fc-event-container',true).first();
19824                     var cg = ctr.createChild(cfg);
19825
19826                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19827                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19828
19829                     var r = (c.more.length) ? 1 : 0;
19830                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19831                     cg.setWidth(ebox.right - sbox.x -2);
19832
19833                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19834                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19835                     cg.on('click', _this.onEventClick, _this, ev);
19836
19837                     ev.els.push(cg);
19838                     
19839                 }
19840                 
19841             }
19842             
19843             
19844             if(c.more.length){
19845                 var  cfg = {
19846                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19847                     style : 'position: absolute',
19848                     unselectable : "on",
19849                     cn : [
19850                         {
19851                             cls: 'fc-event-inner',
19852                             cn : [
19853                                 {
19854                                   tag:'span',
19855                                   cls: 'fc-event-title',
19856                                   html : 'More'
19857                                 }
19858
19859
19860                             ]
19861                         },
19862                         {
19863                             cls: 'ui-resizable-handle ui-resizable-e',
19864                             html : '&nbsp;&nbsp;&nbsp'
19865                         }
19866
19867                     ]
19868                 };
19869
19870                 var ctr = _this.el.select('.fc-event-container',true).first();
19871                 var cg = ctr.createChild(cfg);
19872
19873                 var sbox = c.select('.fc-day-content',true).first().getBox();
19874                 var ebox = c.select('.fc-day-content',true).first().getBox();
19875                 //Roo.log(cg);
19876                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19877                 cg.setWidth(ebox.right - sbox.x -2);
19878
19879                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19880                 
19881             }
19882             
19883         });
19884         
19885         
19886         
19887     },
19888     
19889     onEventEnter: function (e, el,event,d) {
19890         this.fireEvent('evententer', this, el, event);
19891     },
19892     
19893     onEventLeave: function (e, el,event,d) {
19894         this.fireEvent('eventleave', this, el, event);
19895     },
19896     
19897     onEventClick: function (e, el,event,d) {
19898         this.fireEvent('eventclick', this, el, event);
19899     },
19900     
19901     onMonthChange: function () {
19902         this.store.load();
19903     },
19904     
19905     onMoreEventClick: function(e, el, more)
19906     {
19907         var _this = this;
19908         
19909         this.calpopover.placement = 'right';
19910         this.calpopover.setTitle('More');
19911         
19912         this.calpopover.setContent('');
19913         
19914         var ctr = this.calpopover.el.select('.popover-content', true).first();
19915         
19916         Roo.each(more, function(m){
19917             var cfg = {
19918                 cls : 'fc-event-hori fc-event-draggable',
19919                 html : m.title
19920             };
19921             var cg = ctr.createChild(cfg);
19922             
19923             cg.on('click', _this.onEventClick, _this, m);
19924         });
19925         
19926         this.calpopover.show(el);
19927         
19928         
19929     },
19930     
19931     onLoad: function () 
19932     {   
19933         this.calevents = [];
19934         var cal = this;
19935         
19936         if(this.store.getCount() > 0){
19937             this.store.data.each(function(d){
19938                cal.addItem({
19939                     id : d.data.id,
19940                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19941                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19942                     time : d.data.start_time,
19943                     title : d.data.title,
19944                     description : d.data.description,
19945                     venue : d.data.venue
19946                 });
19947             });
19948         }
19949         
19950         this.renderEvents();
19951         
19952         if(this.calevents.length && this.loadMask){
19953             this.maskEl.hide();
19954         }
19955     },
19956     
19957     onBeforeLoad: function()
19958     {
19959         this.clearEvents();
19960         if(this.loadMask){
19961             this.maskEl.show();
19962         }
19963     }
19964 });
19965
19966  
19967  /*
19968  * - LGPL
19969  *
19970  * element
19971  * 
19972  */
19973
19974 /**
19975  * @class Roo.bootstrap.Popover
19976  * @extends Roo.bootstrap.Component
19977  * Bootstrap Popover class
19978  * @cfg {String} html contents of the popover   (or false to use children..)
19979  * @cfg {String} title of popover (or false to hide)
19980  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19981  * @cfg {String} trigger click || hover (or false to trigger manually)
19982  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19983  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19984  *      - if false and it has a 'parent' then it will be automatically added to that element
19985  *      - if string - Roo.get  will be called 
19986  * @cfg {Number} delay - delay before showing
19987  
19988  * @constructor
19989  * Create a new Popover
19990  * @param {Object} config The config object
19991  */
19992
19993 Roo.bootstrap.Popover = function(config){
19994     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19995     
19996     this.addEvents({
19997         // raw events
19998          /**
19999          * @event show
20000          * After the popover show
20001          * 
20002          * @param {Roo.bootstrap.Popover} this
20003          */
20004         "show" : true,
20005         /**
20006          * @event hide
20007          * After the popover hide
20008          * 
20009          * @param {Roo.bootstrap.Popover} this
20010          */
20011         "hide" : true
20012     });
20013 };
20014
20015 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20016     
20017     title: false,
20018     html: false,
20019     
20020     placement : 'right',
20021     trigger : 'hover', // hover
20022     modal : false,
20023     delay : 0,
20024     
20025     over: false,
20026     
20027     can_build_overlaid : false,
20028     
20029     maskEl : false, // the mask element
20030     headerEl : false,
20031     contentEl : false,
20032     alignEl : false, // when show is called with an element - this get's stored.
20033     
20034     getChildContainer : function()
20035     {
20036         return this.contentEl;
20037         
20038     },
20039     getPopoverHeader : function()
20040     {
20041         this.title = true; // flag not to hide it..
20042         this.headerEl.addClass('p-0');
20043         return this.headerEl
20044     },
20045     
20046     
20047     getAutoCreate : function(){
20048          
20049         var cfg = {
20050            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20051            style: 'display:block',
20052            cn : [
20053                 {
20054                     cls : 'arrow'
20055                 },
20056                 {
20057                     cls : 'popover-inner ',
20058                     cn : [
20059                         {
20060                             tag: 'h3',
20061                             cls: 'popover-title popover-header',
20062                             html : this.title === false ? '' : this.title
20063                         },
20064                         {
20065                             cls : 'popover-content popover-body '  + (this.cls || ''),
20066                             html : this.html || ''
20067                         }
20068                     ]
20069                     
20070                 }
20071            ]
20072         };
20073         
20074         return cfg;
20075     },
20076     /**
20077      * @param {string} the title
20078      */
20079     setTitle: function(str)
20080     {
20081         this.title = str;
20082         if (this.el) {
20083             this.headerEl.dom.innerHTML = str;
20084         }
20085         
20086     },
20087     /**
20088      * @param {string} the body content
20089      */
20090     setContent: function(str)
20091     {
20092         this.html = str;
20093         if (this.contentEl) {
20094             this.contentEl.dom.innerHTML = str;
20095         }
20096         
20097     },
20098     // as it get's added to the bottom of the page.
20099     onRender : function(ct, position)
20100     {
20101         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20102         
20103         
20104         
20105         if(!this.el){
20106             var cfg = Roo.apply({},  this.getAutoCreate());
20107             cfg.id = Roo.id();
20108             
20109             if (this.cls) {
20110                 cfg.cls += ' ' + this.cls;
20111             }
20112             if (this.style) {
20113                 cfg.style = this.style;
20114             }
20115             //Roo.log("adding to ");
20116             this.el = Roo.get(document.body).createChild(cfg, position);
20117 //            Roo.log(this.el);
20118         }
20119         
20120         this.contentEl = this.el.select('.popover-content',true).first();
20121         this.headerEl =  this.el.select('.popover-title',true).first();
20122         
20123         var nitems = [];
20124         if(typeof(this.items) != 'undefined'){
20125             var items = this.items;
20126             delete this.items;
20127
20128             for(var i =0;i < items.length;i++) {
20129                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20130             }
20131         }
20132
20133         this.items = nitems;
20134         
20135         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20136         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20137         
20138         
20139         
20140         this.initEvents();
20141     },
20142     
20143     resizeMask : function()
20144     {
20145         this.maskEl.setSize(
20146             Roo.lib.Dom.getViewWidth(true),
20147             Roo.lib.Dom.getViewHeight(true)
20148         );
20149     },
20150     
20151     initEvents : function()
20152     {
20153         
20154         if (!this.modal) { 
20155             Roo.bootstrap.Popover.register(this);
20156         }
20157          
20158         this.arrowEl = this.el.select('.arrow',true).first();
20159         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20160         this.el.enableDisplayMode('block');
20161         this.el.hide();
20162  
20163         
20164         if (this.over === false && !this.parent()) {
20165             return; 
20166         }
20167         if (this.triggers === false) {
20168             return;
20169         }
20170          
20171         // support parent
20172         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20173         var triggers = this.trigger ? this.trigger.split(' ') : [];
20174         Roo.each(triggers, function(trigger) {
20175         
20176             if (trigger == 'click') {
20177                 on_el.on('click', this.toggle, this);
20178             } else if (trigger != 'manual') {
20179                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20180                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20181       
20182                 on_el.on(eventIn  ,this.enter, this);
20183                 on_el.on(eventOut, this.leave, this);
20184             }
20185         }, this);
20186     },
20187     
20188     
20189     // private
20190     timeout : null,
20191     hoverState : null,
20192     
20193     toggle : function () {
20194         this.hoverState == 'in' ? this.leave() : this.enter();
20195     },
20196     
20197     enter : function () {
20198         
20199         clearTimeout(this.timeout);
20200     
20201         this.hoverState = 'in';
20202     
20203         if (!this.delay || !this.delay.show) {
20204             this.show();
20205             return;
20206         }
20207         var _t = this;
20208         this.timeout = setTimeout(function () {
20209             if (_t.hoverState == 'in') {
20210                 _t.show();
20211             }
20212         }, this.delay.show)
20213     },
20214     
20215     leave : function() {
20216         clearTimeout(this.timeout);
20217     
20218         this.hoverState = 'out';
20219     
20220         if (!this.delay || !this.delay.hide) {
20221             this.hide();
20222             return;
20223         }
20224         var _t = this;
20225         this.timeout = setTimeout(function () {
20226             if (_t.hoverState == 'out') {
20227                 _t.hide();
20228             }
20229         }, this.delay.hide)
20230     },
20231     /**
20232      * Show the popover
20233      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20234      * @param {string} (left|right|top|bottom) position
20235      */
20236     show : function (on_el, placement)
20237     {
20238         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20239         on_el = on_el || false; // default to false
20240          
20241         if (!on_el) {
20242             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20243                 on_el = this.parent().el;
20244             } else if (this.over) {
20245                 on_el = Roo.get(this.over);
20246             }
20247             
20248         }
20249         
20250         this.alignEl = Roo.get( on_el );
20251
20252         if (!this.el) {
20253             this.render(document.body);
20254         }
20255         
20256         
20257          
20258         
20259         if (this.title === false) {
20260             this.headerEl.hide();
20261         }
20262         
20263        
20264         this.el.show();
20265         this.el.dom.style.display = 'block';
20266          
20267  
20268         if (this.alignEl) {
20269             this.updatePosition(this.placement, true);
20270              
20271         } else {
20272             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20273             var es = this.el.getSize();
20274             var x = Roo.lib.Dom.getViewWidth()/2;
20275             var y = Roo.lib.Dom.getViewHeight()/2;
20276             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20277             
20278         }
20279
20280         
20281         //var arrow = this.el.select('.arrow',true).first();
20282         //arrow.set(align[2], 
20283         
20284         this.el.addClass('in');
20285         
20286          
20287         
20288         this.hoverState = 'in';
20289         
20290         if (this.modal) {
20291             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20292             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20293             this.maskEl.dom.style.display = 'block';
20294             this.maskEl.addClass('show');
20295         }
20296         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20297  
20298         this.fireEvent('show', this);
20299         
20300     },
20301     /**
20302      * fire this manually after loading a grid in the table for example
20303      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20304      * @param {Boolean} try and move it if we cant get right position.
20305      */
20306     updatePosition : function(placement, try_move)
20307     {
20308         // allow for calling with no parameters
20309         placement = placement   ? placement :  this.placement;
20310         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20311         
20312         this.el.removeClass([
20313             'fade','top','bottom', 'left', 'right','in',
20314             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20315         ]);
20316         this.el.addClass(placement + ' bs-popover-' + placement);
20317         
20318         if (!this.alignEl ) {
20319             return false;
20320         }
20321         
20322         switch (placement) {
20323             case 'right':
20324                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20325                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20326                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20327                     //normal display... or moved up/down.
20328                     this.el.setXY(offset);
20329                     var xy = this.alignEl.getAnchorXY('tr', false);
20330                     xy[0]+=2;xy[1]+=5;
20331                     this.arrowEl.setXY(xy);
20332                     return true;
20333                 }
20334                 // continue through...
20335                 return this.updatePosition('left', false);
20336                 
20337             
20338             case 'left':
20339                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20340                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20341                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20342                     //normal display... or moved up/down.
20343                     this.el.setXY(offset);
20344                     var xy = this.alignEl.getAnchorXY('tl', false);
20345                     xy[0]-=10;xy[1]+=5; // << fix me
20346                     this.arrowEl.setXY(xy);
20347                     return true;
20348                 }
20349                 // call self...
20350                 return this.updatePosition('right', false);
20351             
20352             case 'top':
20353                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20354                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20355                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20356                     //normal display... or moved up/down.
20357                     this.el.setXY(offset);
20358                     var xy = this.alignEl.getAnchorXY('t', false);
20359                     xy[1]-=10; // << fix me
20360                     this.arrowEl.setXY(xy);
20361                     return true;
20362                 }
20363                 // fall through
20364                return this.updatePosition('bottom', false);
20365             
20366             case 'bottom':
20367                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20368                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20369                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20370                     //normal display... or moved up/down.
20371                     this.el.setXY(offset);
20372                     var xy = this.alignEl.getAnchorXY('b', false);
20373                      xy[1]+=2; // << fix me
20374                     this.arrowEl.setXY(xy);
20375                     return true;
20376                 }
20377                 // fall through
20378                 return this.updatePosition('top', false);
20379                 
20380             
20381         }
20382         
20383         
20384         return false;
20385     },
20386     
20387     hide : function()
20388     {
20389         this.el.setXY([0,0]);
20390         this.el.removeClass('in');
20391         this.el.hide();
20392         this.hoverState = null;
20393         this.maskEl.hide(); // always..
20394         this.fireEvent('hide', this);
20395     }
20396     
20397 });
20398
20399
20400 Roo.apply(Roo.bootstrap.Popover, {
20401
20402     alignment : {
20403         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20404         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20405         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20406         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20407     },
20408     
20409     zIndex : 20001,
20410
20411     clickHander : false,
20412     
20413     
20414
20415     onMouseDown : function(e)
20416     {
20417         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20418             /// what is nothing is showing..
20419             this.hideAll();
20420         }
20421          
20422     },
20423     
20424     
20425     popups : [],
20426     
20427     register : function(popup)
20428     {
20429         if (!Roo.bootstrap.Popover.clickHandler) {
20430             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20431         }
20432         // hide other popups.
20433         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20434         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20435         this.hideAll(); //<< why?
20436         //this.popups.push(popup);
20437     },
20438     hideAll : function()
20439     {
20440         this.popups.forEach(function(p) {
20441             p.hide();
20442         });
20443     },
20444     onShow : function() {
20445         Roo.bootstrap.Popover.popups.push(this);
20446     },
20447     onHide : function() {
20448         Roo.bootstrap.Popover.popups.remove(this);
20449     } 
20450
20451 });/*
20452  * - LGPL
20453  *
20454  * Card header - holder for the card header elements.
20455  * 
20456  */
20457
20458 /**
20459  * @class Roo.bootstrap.PopoverNav
20460  * @extends Roo.bootstrap.NavGroup
20461  * Bootstrap Popover header navigation class
20462  * @constructor
20463  * Create a new Popover Header Navigation 
20464  * @param {Object} config The config object
20465  */
20466
20467 Roo.bootstrap.PopoverNav = function(config){
20468     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20469 };
20470
20471 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20472     
20473     
20474     container_method : 'getPopoverHeader' 
20475     
20476      
20477     
20478     
20479    
20480 });
20481
20482  
20483
20484  /*
20485  * - LGPL
20486  *
20487  * Progress
20488  * 
20489  */
20490
20491 /**
20492  * @class Roo.bootstrap.Progress
20493  * @extends Roo.bootstrap.Component
20494  * Bootstrap Progress class
20495  * @cfg {Boolean} striped striped of the progress bar
20496  * @cfg {Boolean} active animated of the progress bar
20497  * 
20498  * 
20499  * @constructor
20500  * Create a new Progress
20501  * @param {Object} config The config object
20502  */
20503
20504 Roo.bootstrap.Progress = function(config){
20505     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20506 };
20507
20508 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20509     
20510     striped : false,
20511     active: false,
20512     
20513     getAutoCreate : function(){
20514         var cfg = {
20515             tag: 'div',
20516             cls: 'progress'
20517         };
20518         
20519         
20520         if(this.striped){
20521             cfg.cls += ' progress-striped';
20522         }
20523       
20524         if(this.active){
20525             cfg.cls += ' active';
20526         }
20527         
20528         
20529         return cfg;
20530     }
20531    
20532 });
20533
20534  
20535
20536  /*
20537  * - LGPL
20538  *
20539  * ProgressBar
20540  * 
20541  */
20542
20543 /**
20544  * @class Roo.bootstrap.ProgressBar
20545  * @extends Roo.bootstrap.Component
20546  * Bootstrap ProgressBar class
20547  * @cfg {Number} aria_valuenow aria-value now
20548  * @cfg {Number} aria_valuemin aria-value min
20549  * @cfg {Number} aria_valuemax aria-value max
20550  * @cfg {String} label label for the progress bar
20551  * @cfg {String} panel (success | info | warning | danger )
20552  * @cfg {String} role role of the progress bar
20553  * @cfg {String} sr_only text
20554  * 
20555  * 
20556  * @constructor
20557  * Create a new ProgressBar
20558  * @param {Object} config The config object
20559  */
20560
20561 Roo.bootstrap.ProgressBar = function(config){
20562     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20563 };
20564
20565 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20566     
20567     aria_valuenow : 0,
20568     aria_valuemin : 0,
20569     aria_valuemax : 100,
20570     label : false,
20571     panel : false,
20572     role : false,
20573     sr_only: false,
20574     
20575     getAutoCreate : function()
20576     {
20577         
20578         var cfg = {
20579             tag: 'div',
20580             cls: 'progress-bar',
20581             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20582         };
20583         
20584         if(this.sr_only){
20585             cfg.cn = {
20586                 tag: 'span',
20587                 cls: 'sr-only',
20588                 html: this.sr_only
20589             }
20590         }
20591         
20592         if(this.role){
20593             cfg.role = this.role;
20594         }
20595         
20596         if(this.aria_valuenow){
20597             cfg['aria-valuenow'] = this.aria_valuenow;
20598         }
20599         
20600         if(this.aria_valuemin){
20601             cfg['aria-valuemin'] = this.aria_valuemin;
20602         }
20603         
20604         if(this.aria_valuemax){
20605             cfg['aria-valuemax'] = this.aria_valuemax;
20606         }
20607         
20608         if(this.label && !this.sr_only){
20609             cfg.html = this.label;
20610         }
20611         
20612         if(this.panel){
20613             cfg.cls += ' progress-bar-' + this.panel;
20614         }
20615         
20616         return cfg;
20617     },
20618     
20619     update : function(aria_valuenow)
20620     {
20621         this.aria_valuenow = aria_valuenow;
20622         
20623         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20624     }
20625    
20626 });
20627
20628  
20629
20630  /*
20631  * - LGPL
20632  *
20633  * column
20634  * 
20635  */
20636
20637 /**
20638  * @class Roo.bootstrap.TabGroup
20639  * @extends Roo.bootstrap.Column
20640  * Bootstrap Column class
20641  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20642  * @cfg {Boolean} carousel true to make the group behave like a carousel
20643  * @cfg {Boolean} bullets show bullets for the panels
20644  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20645  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20646  * @cfg {Boolean} showarrow (true|false) show arrow default true
20647  * 
20648  * @constructor
20649  * Create a new TabGroup
20650  * @param {Object} config The config object
20651  */
20652
20653 Roo.bootstrap.TabGroup = function(config){
20654     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20655     if (!this.navId) {
20656         this.navId = Roo.id();
20657     }
20658     this.tabs = [];
20659     Roo.bootstrap.TabGroup.register(this);
20660     
20661 };
20662
20663 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20664     
20665     carousel : false,
20666     transition : false,
20667     bullets : 0,
20668     timer : 0,
20669     autoslide : false,
20670     slideFn : false,
20671     slideOnTouch : false,
20672     showarrow : true,
20673     
20674     getAutoCreate : function()
20675     {
20676         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20677         
20678         cfg.cls += ' tab-content';
20679         
20680         if (this.carousel) {
20681             cfg.cls += ' carousel slide';
20682             
20683             cfg.cn = [{
20684                cls : 'carousel-inner',
20685                cn : []
20686             }];
20687         
20688             if(this.bullets  && !Roo.isTouch){
20689                 
20690                 var bullets = {
20691                     cls : 'carousel-bullets',
20692                     cn : []
20693                 };
20694                
20695                 if(this.bullets_cls){
20696                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20697                 }
20698                 
20699                 bullets.cn.push({
20700                     cls : 'clear'
20701                 });
20702                 
20703                 cfg.cn[0].cn.push(bullets);
20704             }
20705             
20706             if(this.showarrow){
20707                 cfg.cn[0].cn.push({
20708                     tag : 'div',
20709                     class : 'carousel-arrow',
20710                     cn : [
20711                         {
20712                             tag : 'div',
20713                             class : 'carousel-prev',
20714                             cn : [
20715                                 {
20716                                     tag : 'i',
20717                                     class : 'fa fa-chevron-left'
20718                                 }
20719                             ]
20720                         },
20721                         {
20722                             tag : 'div',
20723                             class : 'carousel-next',
20724                             cn : [
20725                                 {
20726                                     tag : 'i',
20727                                     class : 'fa fa-chevron-right'
20728                                 }
20729                             ]
20730                         }
20731                     ]
20732                 });
20733             }
20734             
20735         }
20736         
20737         return cfg;
20738     },
20739     
20740     initEvents:  function()
20741     {
20742 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20743 //            this.el.on("touchstart", this.onTouchStart, this);
20744 //        }
20745         
20746         if(this.autoslide){
20747             var _this = this;
20748             
20749             this.slideFn = window.setInterval(function() {
20750                 _this.showPanelNext();
20751             }, this.timer);
20752         }
20753         
20754         if(this.showarrow){
20755             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20756             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20757         }
20758         
20759         
20760     },
20761     
20762 //    onTouchStart : function(e, el, o)
20763 //    {
20764 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20765 //            return;
20766 //        }
20767 //        
20768 //        this.showPanelNext();
20769 //    },
20770     
20771     
20772     getChildContainer : function()
20773     {
20774         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20775     },
20776     
20777     /**
20778     * register a Navigation item
20779     * @param {Roo.bootstrap.NavItem} the navitem to add
20780     */
20781     register : function(item)
20782     {
20783         this.tabs.push( item);
20784         item.navId = this.navId; // not really needed..
20785         this.addBullet();
20786     
20787     },
20788     
20789     getActivePanel : function()
20790     {
20791         var r = false;
20792         Roo.each(this.tabs, function(t) {
20793             if (t.active) {
20794                 r = t;
20795                 return false;
20796             }
20797             return null;
20798         });
20799         return r;
20800         
20801     },
20802     getPanelByName : function(n)
20803     {
20804         var r = false;
20805         Roo.each(this.tabs, function(t) {
20806             if (t.tabId == n) {
20807                 r = t;
20808                 return false;
20809             }
20810             return null;
20811         });
20812         return r;
20813     },
20814     indexOfPanel : function(p)
20815     {
20816         var r = false;
20817         Roo.each(this.tabs, function(t,i) {
20818             if (t.tabId == p.tabId) {
20819                 r = i;
20820                 return false;
20821             }
20822             return null;
20823         });
20824         return r;
20825     },
20826     /**
20827      * show a specific panel
20828      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20829      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20830      */
20831     showPanel : function (pan)
20832     {
20833         if(this.transition || typeof(pan) == 'undefined'){
20834             Roo.log("waiting for the transitionend");
20835             return false;
20836         }
20837         
20838         if (typeof(pan) == 'number') {
20839             pan = this.tabs[pan];
20840         }
20841         
20842         if (typeof(pan) == 'string') {
20843             pan = this.getPanelByName(pan);
20844         }
20845         
20846         var cur = this.getActivePanel();
20847         
20848         if(!pan || !cur){
20849             Roo.log('pan or acitve pan is undefined');
20850             return false;
20851         }
20852         
20853         if (pan.tabId == this.getActivePanel().tabId) {
20854             return true;
20855         }
20856         
20857         if (false === cur.fireEvent('beforedeactivate')) {
20858             return false;
20859         }
20860         
20861         if(this.bullets > 0 && !Roo.isTouch){
20862             this.setActiveBullet(this.indexOfPanel(pan));
20863         }
20864         
20865         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20866             
20867             //class="carousel-item carousel-item-next carousel-item-left"
20868             
20869             this.transition = true;
20870             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20871             var lr = dir == 'next' ? 'left' : 'right';
20872             pan.el.addClass(dir); // or prev
20873             pan.el.addClass('carousel-item-' + dir); // or prev
20874             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20875             cur.el.addClass(lr); // or right
20876             pan.el.addClass(lr);
20877             cur.el.addClass('carousel-item-' +lr); // or right
20878             pan.el.addClass('carousel-item-' +lr);
20879             
20880             
20881             var _this = this;
20882             cur.el.on('transitionend', function() {
20883                 Roo.log("trans end?");
20884                 
20885                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20886                 pan.setActive(true);
20887                 
20888                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20889                 cur.setActive(false);
20890                 
20891                 _this.transition = false;
20892                 
20893             }, this, { single:  true } );
20894             
20895             return true;
20896         }
20897         
20898         cur.setActive(false);
20899         pan.setActive(true);
20900         
20901         return true;
20902         
20903     },
20904     showPanelNext : function()
20905     {
20906         var i = this.indexOfPanel(this.getActivePanel());
20907         
20908         if (i >= this.tabs.length - 1 && !this.autoslide) {
20909             return;
20910         }
20911         
20912         if (i >= this.tabs.length - 1 && this.autoslide) {
20913             i = -1;
20914         }
20915         
20916         this.showPanel(this.tabs[i+1]);
20917     },
20918     
20919     showPanelPrev : function()
20920     {
20921         var i = this.indexOfPanel(this.getActivePanel());
20922         
20923         if (i  < 1 && !this.autoslide) {
20924             return;
20925         }
20926         
20927         if (i < 1 && this.autoslide) {
20928             i = this.tabs.length;
20929         }
20930         
20931         this.showPanel(this.tabs[i-1]);
20932     },
20933     
20934     
20935     addBullet: function()
20936     {
20937         if(!this.bullets || Roo.isTouch){
20938             return;
20939         }
20940         var ctr = this.el.select('.carousel-bullets',true).first();
20941         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20942         var bullet = ctr.createChild({
20943             cls : 'bullet bullet-' + i
20944         },ctr.dom.lastChild);
20945         
20946         
20947         var _this = this;
20948         
20949         bullet.on('click', (function(e, el, o, ii, t){
20950
20951             e.preventDefault();
20952
20953             this.showPanel(ii);
20954
20955             if(this.autoslide && this.slideFn){
20956                 clearInterval(this.slideFn);
20957                 this.slideFn = window.setInterval(function() {
20958                     _this.showPanelNext();
20959                 }, this.timer);
20960             }
20961
20962         }).createDelegate(this, [i, bullet], true));
20963                 
20964         
20965     },
20966      
20967     setActiveBullet : function(i)
20968     {
20969         if(Roo.isTouch){
20970             return;
20971         }
20972         
20973         Roo.each(this.el.select('.bullet', true).elements, function(el){
20974             el.removeClass('selected');
20975         });
20976
20977         var bullet = this.el.select('.bullet-' + i, true).first();
20978         
20979         if(!bullet){
20980             return;
20981         }
20982         
20983         bullet.addClass('selected');
20984     }
20985     
20986     
20987   
20988 });
20989
20990  
20991
20992  
20993  
20994 Roo.apply(Roo.bootstrap.TabGroup, {
20995     
20996     groups: {},
20997      /**
20998     * register a Navigation Group
20999     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21000     */
21001     register : function(navgrp)
21002     {
21003         this.groups[navgrp.navId] = navgrp;
21004         
21005     },
21006     /**
21007     * fetch a Navigation Group based on the navigation ID
21008     * if one does not exist , it will get created.
21009     * @param {string} the navgroup to add
21010     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21011     */
21012     get: function(navId) {
21013         if (typeof(this.groups[navId]) == 'undefined') {
21014             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21015         }
21016         return this.groups[navId] ;
21017     }
21018     
21019     
21020     
21021 });
21022
21023  /*
21024  * - LGPL
21025  *
21026  * TabPanel
21027  * 
21028  */
21029
21030 /**
21031  * @class Roo.bootstrap.TabPanel
21032  * @extends Roo.bootstrap.Component
21033  * Bootstrap TabPanel class
21034  * @cfg {Boolean} active panel active
21035  * @cfg {String} html panel content
21036  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21037  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21038  * @cfg {String} href click to link..
21039  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21040  * 
21041  * 
21042  * @constructor
21043  * Create a new TabPanel
21044  * @param {Object} config The config object
21045  */
21046
21047 Roo.bootstrap.TabPanel = function(config){
21048     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21049     this.addEvents({
21050         /**
21051              * @event changed
21052              * Fires when the active status changes
21053              * @param {Roo.bootstrap.TabPanel} this
21054              * @param {Boolean} state the new state
21055             
21056          */
21057         'changed': true,
21058         /**
21059              * @event beforedeactivate
21060              * Fires before a tab is de-activated - can be used to do validation on a form.
21061              * @param {Roo.bootstrap.TabPanel} this
21062              * @return {Boolean} false if there is an error
21063             
21064          */
21065         'beforedeactivate': true
21066      });
21067     
21068     this.tabId = this.tabId || Roo.id();
21069   
21070 };
21071
21072 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21073     
21074     active: false,
21075     html: false,
21076     tabId: false,
21077     navId : false,
21078     href : '',
21079     touchSlide : false,
21080     getAutoCreate : function(){
21081         
21082         
21083         var cfg = {
21084             tag: 'div',
21085             // item is needed for carousel - not sure if it has any effect otherwise
21086             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21087             html: this.html || ''
21088         };
21089         
21090         if(this.active){
21091             cfg.cls += ' active';
21092         }
21093         
21094         if(this.tabId){
21095             cfg.tabId = this.tabId;
21096         }
21097         
21098         
21099         
21100         return cfg;
21101     },
21102     
21103     initEvents:  function()
21104     {
21105         var p = this.parent();
21106         
21107         this.navId = this.navId || p.navId;
21108         
21109         if (typeof(this.navId) != 'undefined') {
21110             // not really needed.. but just in case.. parent should be a NavGroup.
21111             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21112             
21113             tg.register(this);
21114             
21115             var i = tg.tabs.length - 1;
21116             
21117             if(this.active && tg.bullets > 0 && i < tg.bullets){
21118                 tg.setActiveBullet(i);
21119             }
21120         }
21121         
21122         this.el.on('click', this.onClick, this);
21123         
21124         if(Roo.isTouch && this.touchSlide){
21125             this.el.on("touchstart", this.onTouchStart, this);
21126             this.el.on("touchmove", this.onTouchMove, this);
21127             this.el.on("touchend", this.onTouchEnd, this);
21128         }
21129         
21130     },
21131     
21132     onRender : function(ct, position)
21133     {
21134         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21135     },
21136     
21137     setActive : function(state)
21138     {
21139         Roo.log("panel - set active " + this.tabId + "=" + state);
21140         
21141         this.active = state;
21142         if (!state) {
21143             this.el.removeClass('active');
21144             
21145         } else  if (!this.el.hasClass('active')) {
21146             this.el.addClass('active');
21147         }
21148         
21149         this.fireEvent('changed', this, state);
21150     },
21151     
21152     onClick : function(e)
21153     {
21154         e.preventDefault();
21155         
21156         if(!this.href.length){
21157             return;
21158         }
21159         
21160         window.location.href = this.href;
21161     },
21162     
21163     startX : 0,
21164     startY : 0,
21165     endX : 0,
21166     endY : 0,
21167     swiping : false,
21168     
21169     onTouchStart : function(e)
21170     {
21171         this.swiping = false;
21172         
21173         this.startX = e.browserEvent.touches[0].clientX;
21174         this.startY = e.browserEvent.touches[0].clientY;
21175     },
21176     
21177     onTouchMove : function(e)
21178     {
21179         this.swiping = true;
21180         
21181         this.endX = e.browserEvent.touches[0].clientX;
21182         this.endY = e.browserEvent.touches[0].clientY;
21183     },
21184     
21185     onTouchEnd : function(e)
21186     {
21187         if(!this.swiping){
21188             this.onClick(e);
21189             return;
21190         }
21191         
21192         var tabGroup = this.parent();
21193         
21194         if(this.endX > this.startX){ // swiping right
21195             tabGroup.showPanelPrev();
21196             return;
21197         }
21198         
21199         if(this.startX > this.endX){ // swiping left
21200             tabGroup.showPanelNext();
21201             return;
21202         }
21203     }
21204     
21205     
21206 });
21207  
21208
21209  
21210
21211  /*
21212  * - LGPL
21213  *
21214  * DateField
21215  * 
21216  */
21217
21218 /**
21219  * @class Roo.bootstrap.DateField
21220  * @extends Roo.bootstrap.Input
21221  * Bootstrap DateField class
21222  * @cfg {Number} weekStart default 0
21223  * @cfg {String} viewMode default empty, (months|years)
21224  * @cfg {String} minViewMode default empty, (months|years)
21225  * @cfg {Number} startDate default -Infinity
21226  * @cfg {Number} endDate default Infinity
21227  * @cfg {Boolean} todayHighlight default false
21228  * @cfg {Boolean} todayBtn default false
21229  * @cfg {Boolean} calendarWeeks default false
21230  * @cfg {Object} daysOfWeekDisabled default empty
21231  * @cfg {Boolean} singleMode default false (true | false)
21232  * 
21233  * @cfg {Boolean} keyboardNavigation default true
21234  * @cfg {String} language default en
21235  * 
21236  * @constructor
21237  * Create a new DateField
21238  * @param {Object} config The config object
21239  */
21240
21241 Roo.bootstrap.DateField = function(config){
21242     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21243      this.addEvents({
21244             /**
21245              * @event show
21246              * Fires when this field show.
21247              * @param {Roo.bootstrap.DateField} this
21248              * @param {Mixed} date The date value
21249              */
21250             show : true,
21251             /**
21252              * @event show
21253              * Fires when this field hide.
21254              * @param {Roo.bootstrap.DateField} this
21255              * @param {Mixed} date The date value
21256              */
21257             hide : true,
21258             /**
21259              * @event select
21260              * Fires when select a date.
21261              * @param {Roo.bootstrap.DateField} this
21262              * @param {Mixed} date The date value
21263              */
21264             select : true,
21265             /**
21266              * @event beforeselect
21267              * Fires when before select a date.
21268              * @param {Roo.bootstrap.DateField} this
21269              * @param {Mixed} date The date value
21270              */
21271             beforeselect : true
21272         });
21273 };
21274
21275 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21276     
21277     /**
21278      * @cfg {String} format
21279      * The default date format string which can be overriden for localization support.  The format must be
21280      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21281      */
21282     format : "m/d/y",
21283     /**
21284      * @cfg {String} altFormats
21285      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21286      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21287      */
21288     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21289     
21290     weekStart : 0,
21291     
21292     viewMode : '',
21293     
21294     minViewMode : '',
21295     
21296     todayHighlight : false,
21297     
21298     todayBtn: false,
21299     
21300     language: 'en',
21301     
21302     keyboardNavigation: true,
21303     
21304     calendarWeeks: false,
21305     
21306     startDate: -Infinity,
21307     
21308     endDate: Infinity,
21309     
21310     daysOfWeekDisabled: [],
21311     
21312     _events: [],
21313     
21314     singleMode : false,
21315     
21316     UTCDate: function()
21317     {
21318         return new Date(Date.UTC.apply(Date, arguments));
21319     },
21320     
21321     UTCToday: function()
21322     {
21323         var today = new Date();
21324         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21325     },
21326     
21327     getDate: function() {
21328             var d = this.getUTCDate();
21329             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21330     },
21331     
21332     getUTCDate: function() {
21333             return this.date;
21334     },
21335     
21336     setDate: function(d) {
21337             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21338     },
21339     
21340     setUTCDate: function(d) {
21341             this.date = d;
21342             this.setValue(this.formatDate(this.date));
21343     },
21344         
21345     onRender: function(ct, position)
21346     {
21347         
21348         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21349         
21350         this.language = this.language || 'en';
21351         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21352         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21353         
21354         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21355         this.format = this.format || 'm/d/y';
21356         this.isInline = false;
21357         this.isInput = true;
21358         this.component = this.el.select('.add-on', true).first() || false;
21359         this.component = (this.component && this.component.length === 0) ? false : this.component;
21360         this.hasInput = this.component && this.inputEl().length;
21361         
21362         if (typeof(this.minViewMode === 'string')) {
21363             switch (this.minViewMode) {
21364                 case 'months':
21365                     this.minViewMode = 1;
21366                     break;
21367                 case 'years':
21368                     this.minViewMode = 2;
21369                     break;
21370                 default:
21371                     this.minViewMode = 0;
21372                     break;
21373             }
21374         }
21375         
21376         if (typeof(this.viewMode === 'string')) {
21377             switch (this.viewMode) {
21378                 case 'months':
21379                     this.viewMode = 1;
21380                     break;
21381                 case 'years':
21382                     this.viewMode = 2;
21383                     break;
21384                 default:
21385                     this.viewMode = 0;
21386                     break;
21387             }
21388         }
21389                 
21390         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21391         
21392 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21393         
21394         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21395         
21396         this.picker().on('mousedown', this.onMousedown, this);
21397         this.picker().on('click', this.onClick, this);
21398         
21399         this.picker().addClass('datepicker-dropdown');
21400         
21401         this.startViewMode = this.viewMode;
21402         
21403         if(this.singleMode){
21404             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21405                 v.setVisibilityMode(Roo.Element.DISPLAY);
21406                 v.hide();
21407             });
21408             
21409             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21410                 v.setStyle('width', '189px');
21411             });
21412         }
21413         
21414         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21415             if(!this.calendarWeeks){
21416                 v.remove();
21417                 return;
21418             }
21419             
21420             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21421             v.attr('colspan', function(i, val){
21422                 return parseInt(val) + 1;
21423             });
21424         });
21425                         
21426         
21427         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21428         
21429         this.setStartDate(this.startDate);
21430         this.setEndDate(this.endDate);
21431         
21432         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21433         
21434         this.fillDow();
21435         this.fillMonths();
21436         this.update();
21437         this.showMode();
21438         
21439         if(this.isInline) {
21440             this.showPopup();
21441         }
21442     },
21443     
21444     picker : function()
21445     {
21446         return this.pickerEl;
21447 //        return this.el.select('.datepicker', true).first();
21448     },
21449     
21450     fillDow: function()
21451     {
21452         var dowCnt = this.weekStart;
21453         
21454         var dow = {
21455             tag: 'tr',
21456             cn: [
21457                 
21458             ]
21459         };
21460         
21461         if(this.calendarWeeks){
21462             dow.cn.push({
21463                 tag: 'th',
21464                 cls: 'cw',
21465                 html: '&nbsp;'
21466             })
21467         }
21468         
21469         while (dowCnt < this.weekStart + 7) {
21470             dow.cn.push({
21471                 tag: 'th',
21472                 cls: 'dow',
21473                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21474             });
21475         }
21476         
21477         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21478     },
21479     
21480     fillMonths: function()
21481     {    
21482         var i = 0;
21483         var months = this.picker().select('>.datepicker-months td', true).first();
21484         
21485         months.dom.innerHTML = '';
21486         
21487         while (i < 12) {
21488             var month = {
21489                 tag: 'span',
21490                 cls: 'month',
21491                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21492             };
21493             
21494             months.createChild(month);
21495         }
21496         
21497     },
21498     
21499     update: function()
21500     {
21501         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;
21502         
21503         if (this.date < this.startDate) {
21504             this.viewDate = new Date(this.startDate);
21505         } else if (this.date > this.endDate) {
21506             this.viewDate = new Date(this.endDate);
21507         } else {
21508             this.viewDate = new Date(this.date);
21509         }
21510         
21511         this.fill();
21512     },
21513     
21514     fill: function() 
21515     {
21516         var d = new Date(this.viewDate),
21517                 year = d.getUTCFullYear(),
21518                 month = d.getUTCMonth(),
21519                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21520                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21521                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21522                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21523                 currentDate = this.date && this.date.valueOf(),
21524                 today = this.UTCToday();
21525         
21526         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21527         
21528 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21529         
21530 //        this.picker.select('>tfoot th.today').
21531 //                                              .text(dates[this.language].today)
21532 //                                              .toggle(this.todayBtn !== false);
21533     
21534         this.updateNavArrows();
21535         this.fillMonths();
21536                                                 
21537         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21538         
21539         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21540          
21541         prevMonth.setUTCDate(day);
21542         
21543         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21544         
21545         var nextMonth = new Date(prevMonth);
21546         
21547         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21548         
21549         nextMonth = nextMonth.valueOf();
21550         
21551         var fillMonths = false;
21552         
21553         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21554         
21555         while(prevMonth.valueOf() <= nextMonth) {
21556             var clsName = '';
21557             
21558             if (prevMonth.getUTCDay() === this.weekStart) {
21559                 if(fillMonths){
21560                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21561                 }
21562                     
21563                 fillMonths = {
21564                     tag: 'tr',
21565                     cn: []
21566                 };
21567                 
21568                 if(this.calendarWeeks){
21569                     // ISO 8601: First week contains first thursday.
21570                     // ISO also states week starts on Monday, but we can be more abstract here.
21571                     var
21572                     // Start of current week: based on weekstart/current date
21573                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21574                     // Thursday of this week
21575                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21576                     // First Thursday of year, year from thursday
21577                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21578                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21579                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21580                     
21581                     fillMonths.cn.push({
21582                         tag: 'td',
21583                         cls: 'cw',
21584                         html: calWeek
21585                     });
21586                 }
21587             }
21588             
21589             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21590                 clsName += ' old';
21591             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21592                 clsName += ' new';
21593             }
21594             if (this.todayHighlight &&
21595                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21596                 prevMonth.getUTCMonth() == today.getMonth() &&
21597                 prevMonth.getUTCDate() == today.getDate()) {
21598                 clsName += ' today';
21599             }
21600             
21601             if (currentDate && prevMonth.valueOf() === currentDate) {
21602                 clsName += ' active';
21603             }
21604             
21605             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21606                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21607                     clsName += ' disabled';
21608             }
21609             
21610             fillMonths.cn.push({
21611                 tag: 'td',
21612                 cls: 'day ' + clsName,
21613                 html: prevMonth.getDate()
21614             });
21615             
21616             prevMonth.setDate(prevMonth.getDate()+1);
21617         }
21618           
21619         var currentYear = this.date && this.date.getUTCFullYear();
21620         var currentMonth = this.date && this.date.getUTCMonth();
21621         
21622         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21623         
21624         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21625             v.removeClass('active');
21626             
21627             if(currentYear === year && k === currentMonth){
21628                 v.addClass('active');
21629             }
21630             
21631             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21632                 v.addClass('disabled');
21633             }
21634             
21635         });
21636         
21637         
21638         year = parseInt(year/10, 10) * 10;
21639         
21640         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21641         
21642         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21643         
21644         year -= 1;
21645         for (var i = -1; i < 11; i++) {
21646             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21647                 tag: 'span',
21648                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21649                 html: year
21650             });
21651             
21652             year += 1;
21653         }
21654     },
21655     
21656     showMode: function(dir) 
21657     {
21658         if (dir) {
21659             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21660         }
21661         
21662         Roo.each(this.picker().select('>div',true).elements, function(v){
21663             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21664             v.hide();
21665         });
21666         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21667     },
21668     
21669     place: function()
21670     {
21671         if(this.isInline) {
21672             return;
21673         }
21674         
21675         this.picker().removeClass(['bottom', 'top']);
21676         
21677         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21678             /*
21679              * place to the top of element!
21680              *
21681              */
21682             
21683             this.picker().addClass('top');
21684             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21685             
21686             return;
21687         }
21688         
21689         this.picker().addClass('bottom');
21690         
21691         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21692     },
21693     
21694     parseDate : function(value)
21695     {
21696         if(!value || value instanceof Date){
21697             return value;
21698         }
21699         var v = Date.parseDate(value, this.format);
21700         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21701             v = Date.parseDate(value, 'Y-m-d');
21702         }
21703         if(!v && this.altFormats){
21704             if(!this.altFormatsArray){
21705                 this.altFormatsArray = this.altFormats.split("|");
21706             }
21707             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21708                 v = Date.parseDate(value, this.altFormatsArray[i]);
21709             }
21710         }
21711         return v;
21712     },
21713     
21714     formatDate : function(date, fmt)
21715     {   
21716         return (!date || !(date instanceof Date)) ?
21717         date : date.dateFormat(fmt || this.format);
21718     },
21719     
21720     onFocus : function()
21721     {
21722         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21723         this.showPopup();
21724     },
21725     
21726     onBlur : function()
21727     {
21728         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21729         
21730         var d = this.inputEl().getValue();
21731         
21732         this.setValue(d);
21733                 
21734         this.hidePopup();
21735     },
21736     
21737     showPopup : function()
21738     {
21739         this.picker().show();
21740         this.update();
21741         this.place();
21742         
21743         this.fireEvent('showpopup', this, this.date);
21744     },
21745     
21746     hidePopup : function()
21747     {
21748         if(this.isInline) {
21749             return;
21750         }
21751         this.picker().hide();
21752         this.viewMode = this.startViewMode;
21753         this.showMode();
21754         
21755         this.fireEvent('hidepopup', this, this.date);
21756         
21757     },
21758     
21759     onMousedown: function(e)
21760     {
21761         e.stopPropagation();
21762         e.preventDefault();
21763     },
21764     
21765     keyup: function(e)
21766     {
21767         Roo.bootstrap.DateField.superclass.keyup.call(this);
21768         this.update();
21769     },
21770
21771     setValue: function(v)
21772     {
21773         if(this.fireEvent('beforeselect', this, v) !== false){
21774             var d = new Date(this.parseDate(v) ).clearTime();
21775         
21776             if(isNaN(d.getTime())){
21777                 this.date = this.viewDate = '';
21778                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21779                 return;
21780             }
21781
21782             v = this.formatDate(d);
21783
21784             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21785
21786             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21787
21788             this.update();
21789
21790             this.fireEvent('select', this, this.date);
21791         }
21792     },
21793     
21794     getValue: function()
21795     {
21796         return this.formatDate(this.date);
21797     },
21798     
21799     fireKey: function(e)
21800     {
21801         if (!this.picker().isVisible()){
21802             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21803                 this.showPopup();
21804             }
21805             return;
21806         }
21807         
21808         var dateChanged = false,
21809         dir, day, month,
21810         newDate, newViewDate;
21811         
21812         switch(e.keyCode){
21813             case 27: // escape
21814                 this.hidePopup();
21815                 e.preventDefault();
21816                 break;
21817             case 37: // left
21818             case 39: // right
21819                 if (!this.keyboardNavigation) {
21820                     break;
21821                 }
21822                 dir = e.keyCode == 37 ? -1 : 1;
21823                 
21824                 if (e.ctrlKey){
21825                     newDate = this.moveYear(this.date, dir);
21826                     newViewDate = this.moveYear(this.viewDate, dir);
21827                 } else if (e.shiftKey){
21828                     newDate = this.moveMonth(this.date, dir);
21829                     newViewDate = this.moveMonth(this.viewDate, dir);
21830                 } else {
21831                     newDate = new Date(this.date);
21832                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21833                     newViewDate = new Date(this.viewDate);
21834                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21835                 }
21836                 if (this.dateWithinRange(newDate)){
21837                     this.date = newDate;
21838                     this.viewDate = newViewDate;
21839                     this.setValue(this.formatDate(this.date));
21840 //                    this.update();
21841                     e.preventDefault();
21842                     dateChanged = true;
21843                 }
21844                 break;
21845             case 38: // up
21846             case 40: // down
21847                 if (!this.keyboardNavigation) {
21848                     break;
21849                 }
21850                 dir = e.keyCode == 38 ? -1 : 1;
21851                 if (e.ctrlKey){
21852                     newDate = this.moveYear(this.date, dir);
21853                     newViewDate = this.moveYear(this.viewDate, dir);
21854                 } else if (e.shiftKey){
21855                     newDate = this.moveMonth(this.date, dir);
21856                     newViewDate = this.moveMonth(this.viewDate, dir);
21857                 } else {
21858                     newDate = new Date(this.date);
21859                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21860                     newViewDate = new Date(this.viewDate);
21861                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21862                 }
21863                 if (this.dateWithinRange(newDate)){
21864                     this.date = newDate;
21865                     this.viewDate = newViewDate;
21866                     this.setValue(this.formatDate(this.date));
21867 //                    this.update();
21868                     e.preventDefault();
21869                     dateChanged = true;
21870                 }
21871                 break;
21872             case 13: // enter
21873                 this.setValue(this.formatDate(this.date));
21874                 this.hidePopup();
21875                 e.preventDefault();
21876                 break;
21877             case 9: // tab
21878                 this.setValue(this.formatDate(this.date));
21879                 this.hidePopup();
21880                 break;
21881             case 16: // shift
21882             case 17: // ctrl
21883             case 18: // alt
21884                 break;
21885             default :
21886                 this.hidePopup();
21887                 
21888         }
21889     },
21890     
21891     
21892     onClick: function(e) 
21893     {
21894         e.stopPropagation();
21895         e.preventDefault();
21896         
21897         var target = e.getTarget();
21898         
21899         if(target.nodeName.toLowerCase() === 'i'){
21900             target = Roo.get(target).dom.parentNode;
21901         }
21902         
21903         var nodeName = target.nodeName;
21904         var className = target.className;
21905         var html = target.innerHTML;
21906         //Roo.log(nodeName);
21907         
21908         switch(nodeName.toLowerCase()) {
21909             case 'th':
21910                 switch(className) {
21911                     case 'switch':
21912                         this.showMode(1);
21913                         break;
21914                     case 'prev':
21915                     case 'next':
21916                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21917                         switch(this.viewMode){
21918                                 case 0:
21919                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21920                                         break;
21921                                 case 1:
21922                                 case 2:
21923                                         this.viewDate = this.moveYear(this.viewDate, dir);
21924                                         break;
21925                         }
21926                         this.fill();
21927                         break;
21928                     case 'today':
21929                         var date = new Date();
21930                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21931 //                        this.fill()
21932                         this.setValue(this.formatDate(this.date));
21933                         
21934                         this.hidePopup();
21935                         break;
21936                 }
21937                 break;
21938             case 'span':
21939                 if (className.indexOf('disabled') < 0) {
21940                     this.viewDate.setUTCDate(1);
21941                     if (className.indexOf('month') > -1) {
21942                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21943                     } else {
21944                         var year = parseInt(html, 10) || 0;
21945                         this.viewDate.setUTCFullYear(year);
21946                         
21947                     }
21948                     
21949                     if(this.singleMode){
21950                         this.setValue(this.formatDate(this.viewDate));
21951                         this.hidePopup();
21952                         return;
21953                     }
21954                     
21955                     this.showMode(-1);
21956                     this.fill();
21957                 }
21958                 break;
21959                 
21960             case 'td':
21961                 //Roo.log(className);
21962                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21963                     var day = parseInt(html, 10) || 1;
21964                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21965                         month = (this.viewDate || new Date()).getUTCMonth();
21966
21967                     if (className.indexOf('old') > -1) {
21968                         if(month === 0 ){
21969                             month = 11;
21970                             year -= 1;
21971                         }else{
21972                             month -= 1;
21973                         }
21974                     } else if (className.indexOf('new') > -1) {
21975                         if (month == 11) {
21976                             month = 0;
21977                             year += 1;
21978                         } else {
21979                             month += 1;
21980                         }
21981                     }
21982                     //Roo.log([year,month,day]);
21983                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21984                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21985 //                    this.fill();
21986                     //Roo.log(this.formatDate(this.date));
21987                     this.setValue(this.formatDate(this.date));
21988                     this.hidePopup();
21989                 }
21990                 break;
21991         }
21992     },
21993     
21994     setStartDate: function(startDate)
21995     {
21996         this.startDate = startDate || -Infinity;
21997         if (this.startDate !== -Infinity) {
21998             this.startDate = this.parseDate(this.startDate);
21999         }
22000         this.update();
22001         this.updateNavArrows();
22002     },
22003
22004     setEndDate: function(endDate)
22005     {
22006         this.endDate = endDate || Infinity;
22007         if (this.endDate !== Infinity) {
22008             this.endDate = this.parseDate(this.endDate);
22009         }
22010         this.update();
22011         this.updateNavArrows();
22012     },
22013     
22014     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22015     {
22016         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22017         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22018             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22019         }
22020         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22021             return parseInt(d, 10);
22022         });
22023         this.update();
22024         this.updateNavArrows();
22025     },
22026     
22027     updateNavArrows: function() 
22028     {
22029         if(this.singleMode){
22030             return;
22031         }
22032         
22033         var d = new Date(this.viewDate),
22034         year = d.getUTCFullYear(),
22035         month = d.getUTCMonth();
22036         
22037         Roo.each(this.picker().select('.prev', true).elements, function(v){
22038             v.show();
22039             switch (this.viewMode) {
22040                 case 0:
22041
22042                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22043                         v.hide();
22044                     }
22045                     break;
22046                 case 1:
22047                 case 2:
22048                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22049                         v.hide();
22050                     }
22051                     break;
22052             }
22053         });
22054         
22055         Roo.each(this.picker().select('.next', true).elements, function(v){
22056             v.show();
22057             switch (this.viewMode) {
22058                 case 0:
22059
22060                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22061                         v.hide();
22062                     }
22063                     break;
22064                 case 1:
22065                 case 2:
22066                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22067                         v.hide();
22068                     }
22069                     break;
22070             }
22071         })
22072     },
22073     
22074     moveMonth: function(date, dir)
22075     {
22076         if (!dir) {
22077             return date;
22078         }
22079         var new_date = new Date(date.valueOf()),
22080         day = new_date.getUTCDate(),
22081         month = new_date.getUTCMonth(),
22082         mag = Math.abs(dir),
22083         new_month, test;
22084         dir = dir > 0 ? 1 : -1;
22085         if (mag == 1){
22086             test = dir == -1
22087             // If going back one month, make sure month is not current month
22088             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22089             ? function(){
22090                 return new_date.getUTCMonth() == month;
22091             }
22092             // If going forward one month, make sure month is as expected
22093             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22094             : function(){
22095                 return new_date.getUTCMonth() != new_month;
22096             };
22097             new_month = month + dir;
22098             new_date.setUTCMonth(new_month);
22099             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22100             if (new_month < 0 || new_month > 11) {
22101                 new_month = (new_month + 12) % 12;
22102             }
22103         } else {
22104             // For magnitudes >1, move one month at a time...
22105             for (var i=0; i<mag; i++) {
22106                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22107                 new_date = this.moveMonth(new_date, dir);
22108             }
22109             // ...then reset the day, keeping it in the new month
22110             new_month = new_date.getUTCMonth();
22111             new_date.setUTCDate(day);
22112             test = function(){
22113                 return new_month != new_date.getUTCMonth();
22114             };
22115         }
22116         // Common date-resetting loop -- if date is beyond end of month, make it
22117         // end of month
22118         while (test()){
22119             new_date.setUTCDate(--day);
22120             new_date.setUTCMonth(new_month);
22121         }
22122         return new_date;
22123     },
22124
22125     moveYear: function(date, dir)
22126     {
22127         return this.moveMonth(date, dir*12);
22128     },
22129
22130     dateWithinRange: function(date)
22131     {
22132         return date >= this.startDate && date <= this.endDate;
22133     },
22134
22135     
22136     remove: function() 
22137     {
22138         this.picker().remove();
22139     },
22140     
22141     validateValue : function(value)
22142     {
22143         if(this.getVisibilityEl().hasClass('hidden')){
22144             return true;
22145         }
22146         
22147         if(value.length < 1)  {
22148             if(this.allowBlank){
22149                 return true;
22150             }
22151             return false;
22152         }
22153         
22154         if(value.length < this.minLength){
22155             return false;
22156         }
22157         if(value.length > this.maxLength){
22158             return false;
22159         }
22160         if(this.vtype){
22161             var vt = Roo.form.VTypes;
22162             if(!vt[this.vtype](value, this)){
22163                 return false;
22164             }
22165         }
22166         if(typeof this.validator == "function"){
22167             var msg = this.validator(value);
22168             if(msg !== true){
22169                 return false;
22170             }
22171         }
22172         
22173         if(this.regex && !this.regex.test(value)){
22174             return false;
22175         }
22176         
22177         if(typeof(this.parseDate(value)) == 'undefined'){
22178             return false;
22179         }
22180         
22181         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22182             return false;
22183         }      
22184         
22185         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22186             return false;
22187         } 
22188         
22189         
22190         return true;
22191     },
22192     
22193     reset : function()
22194     {
22195         this.date = this.viewDate = '';
22196         
22197         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22198     }
22199    
22200 });
22201
22202 Roo.apply(Roo.bootstrap.DateField,  {
22203     
22204     head : {
22205         tag: 'thead',
22206         cn: [
22207         {
22208             tag: 'tr',
22209             cn: [
22210             {
22211                 tag: 'th',
22212                 cls: 'prev',
22213                 html: '<i class="fa fa-arrow-left"/>'
22214             },
22215             {
22216                 tag: 'th',
22217                 cls: 'switch',
22218                 colspan: '5'
22219             },
22220             {
22221                 tag: 'th',
22222                 cls: 'next',
22223                 html: '<i class="fa fa-arrow-right"/>'
22224             }
22225
22226             ]
22227         }
22228         ]
22229     },
22230     
22231     content : {
22232         tag: 'tbody',
22233         cn: [
22234         {
22235             tag: 'tr',
22236             cn: [
22237             {
22238                 tag: 'td',
22239                 colspan: '7'
22240             }
22241             ]
22242         }
22243         ]
22244     },
22245     
22246     footer : {
22247         tag: 'tfoot',
22248         cn: [
22249         {
22250             tag: 'tr',
22251             cn: [
22252             {
22253                 tag: 'th',
22254                 colspan: '7',
22255                 cls: 'today'
22256             }
22257                     
22258             ]
22259         }
22260         ]
22261     },
22262     
22263     dates:{
22264         en: {
22265             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22266             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22267             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22268             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22269             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22270             today: "Today"
22271         }
22272     },
22273     
22274     modes: [
22275     {
22276         clsName: 'days',
22277         navFnc: 'Month',
22278         navStep: 1
22279     },
22280     {
22281         clsName: 'months',
22282         navFnc: 'FullYear',
22283         navStep: 1
22284     },
22285     {
22286         clsName: 'years',
22287         navFnc: 'FullYear',
22288         navStep: 10
22289     }]
22290 });
22291
22292 Roo.apply(Roo.bootstrap.DateField,  {
22293   
22294     template : {
22295         tag: 'div',
22296         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22297         cn: [
22298         {
22299             tag: 'div',
22300             cls: 'datepicker-days',
22301             cn: [
22302             {
22303                 tag: 'table',
22304                 cls: 'table-condensed',
22305                 cn:[
22306                 Roo.bootstrap.DateField.head,
22307                 {
22308                     tag: 'tbody'
22309                 },
22310                 Roo.bootstrap.DateField.footer
22311                 ]
22312             }
22313             ]
22314         },
22315         {
22316             tag: 'div',
22317             cls: 'datepicker-months',
22318             cn: [
22319             {
22320                 tag: 'table',
22321                 cls: 'table-condensed',
22322                 cn:[
22323                 Roo.bootstrap.DateField.head,
22324                 Roo.bootstrap.DateField.content,
22325                 Roo.bootstrap.DateField.footer
22326                 ]
22327             }
22328             ]
22329         },
22330         {
22331             tag: 'div',
22332             cls: 'datepicker-years',
22333             cn: [
22334             {
22335                 tag: 'table',
22336                 cls: 'table-condensed',
22337                 cn:[
22338                 Roo.bootstrap.DateField.head,
22339                 Roo.bootstrap.DateField.content,
22340                 Roo.bootstrap.DateField.footer
22341                 ]
22342             }
22343             ]
22344         }
22345         ]
22346     }
22347 });
22348
22349  
22350
22351  /*
22352  * - LGPL
22353  *
22354  * TimeField
22355  * 
22356  */
22357
22358 /**
22359  * @class Roo.bootstrap.TimeField
22360  * @extends Roo.bootstrap.Input
22361  * Bootstrap DateField class
22362  * 
22363  * 
22364  * @constructor
22365  * Create a new TimeField
22366  * @param {Object} config The config object
22367  */
22368
22369 Roo.bootstrap.TimeField = function(config){
22370     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22371     this.addEvents({
22372             /**
22373              * @event show
22374              * Fires when this field show.
22375              * @param {Roo.bootstrap.DateField} thisthis
22376              * @param {Mixed} date The date value
22377              */
22378             show : true,
22379             /**
22380              * @event show
22381              * Fires when this field hide.
22382              * @param {Roo.bootstrap.DateField} this
22383              * @param {Mixed} date The date value
22384              */
22385             hide : true,
22386             /**
22387              * @event select
22388              * Fires when select a date.
22389              * @param {Roo.bootstrap.DateField} this
22390              * @param {Mixed} date The date value
22391              */
22392             select : true
22393         });
22394 };
22395
22396 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22397     
22398     /**
22399      * @cfg {String} format
22400      * The default time format string which can be overriden for localization support.  The format must be
22401      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22402      */
22403     format : "H:i",
22404
22405     getAutoCreate : function()
22406     {
22407         this.after = '<i class="fa far fa-clock"></i>';
22408         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22409         
22410          
22411     },
22412     onRender: function(ct, position)
22413     {
22414         
22415         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22416                 
22417         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22418         
22419         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22420         
22421         this.pop = this.picker().select('>.datepicker-time',true).first();
22422         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22423         
22424         this.picker().on('mousedown', this.onMousedown, this);
22425         this.picker().on('click', this.onClick, this);
22426         
22427         this.picker().addClass('datepicker-dropdown');
22428     
22429         this.fillTime();
22430         this.update();
22431             
22432         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22433         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22434         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22435         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22436         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22437         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22438
22439     },
22440     
22441     fireKey: function(e){
22442         if (!this.picker().isVisible()){
22443             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22444                 this.show();
22445             }
22446             return;
22447         }
22448
22449         e.preventDefault();
22450         
22451         switch(e.keyCode){
22452             case 27: // escape
22453                 this.hide();
22454                 break;
22455             case 37: // left
22456             case 39: // right
22457                 this.onTogglePeriod();
22458                 break;
22459             case 38: // up
22460                 this.onIncrementMinutes();
22461                 break;
22462             case 40: // down
22463                 this.onDecrementMinutes();
22464                 break;
22465             case 13: // enter
22466             case 9: // tab
22467                 this.setTime();
22468                 break;
22469         }
22470     },
22471     
22472     onClick: function(e) {
22473         e.stopPropagation();
22474         e.preventDefault();
22475     },
22476     
22477     picker : function()
22478     {
22479         return this.pickerEl;
22480     },
22481     
22482     fillTime: function()
22483     {    
22484         var time = this.pop.select('tbody', true).first();
22485         
22486         time.dom.innerHTML = '';
22487         
22488         time.createChild({
22489             tag: 'tr',
22490             cn: [
22491                 {
22492                     tag: 'td',
22493                     cn: [
22494                         {
22495                             tag: 'a',
22496                             href: '#',
22497                             cls: 'btn',
22498                             cn: [
22499                                 {
22500                                     tag: 'i',
22501                                     cls: 'hours-up fa fas fa-chevron-up'
22502                                 }
22503                             ]
22504                         } 
22505                     ]
22506                 },
22507                 {
22508                     tag: 'td',
22509                     cls: 'separator'
22510                 },
22511                 {
22512                     tag: 'td',
22513                     cn: [
22514                         {
22515                             tag: 'a',
22516                             href: '#',
22517                             cls: 'btn',
22518                             cn: [
22519                                 {
22520                                     tag: 'i',
22521                                     cls: 'minutes-up fa fas fa-chevron-up'
22522                                 }
22523                             ]
22524                         }
22525                     ]
22526                 },
22527                 {
22528                     tag: 'td',
22529                     cls: 'separator'
22530                 }
22531             ]
22532         });
22533         
22534         time.createChild({
22535             tag: 'tr',
22536             cn: [
22537                 {
22538                     tag: 'td',
22539                     cn: [
22540                         {
22541                             tag: 'span',
22542                             cls: 'timepicker-hour',
22543                             html: '00'
22544                         }  
22545                     ]
22546                 },
22547                 {
22548                     tag: 'td',
22549                     cls: 'separator',
22550                     html: ':'
22551                 },
22552                 {
22553                     tag: 'td',
22554                     cn: [
22555                         {
22556                             tag: 'span',
22557                             cls: 'timepicker-minute',
22558                             html: '00'
22559                         }  
22560                     ]
22561                 },
22562                 {
22563                     tag: 'td',
22564                     cls: 'separator'
22565                 },
22566                 {
22567                     tag: 'td',
22568                     cn: [
22569                         {
22570                             tag: 'button',
22571                             type: 'button',
22572                             cls: 'btn btn-primary period',
22573                             html: 'AM'
22574                             
22575                         }
22576                     ]
22577                 }
22578             ]
22579         });
22580         
22581         time.createChild({
22582             tag: 'tr',
22583             cn: [
22584                 {
22585                     tag: 'td',
22586                     cn: [
22587                         {
22588                             tag: 'a',
22589                             href: '#',
22590                             cls: 'btn',
22591                             cn: [
22592                                 {
22593                                     tag: 'span',
22594                                     cls: 'hours-down fa fas fa-chevron-down'
22595                                 }
22596                             ]
22597                         }
22598                     ]
22599                 },
22600                 {
22601                     tag: 'td',
22602                     cls: 'separator'
22603                 },
22604                 {
22605                     tag: 'td',
22606                     cn: [
22607                         {
22608                             tag: 'a',
22609                             href: '#',
22610                             cls: 'btn',
22611                             cn: [
22612                                 {
22613                                     tag: 'span',
22614                                     cls: 'minutes-down fa fas fa-chevron-down'
22615                                 }
22616                             ]
22617                         }
22618                     ]
22619                 },
22620                 {
22621                     tag: 'td',
22622                     cls: 'separator'
22623                 }
22624             ]
22625         });
22626         
22627     },
22628     
22629     update: function()
22630     {
22631         
22632         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22633         
22634         this.fill();
22635     },
22636     
22637     fill: function() 
22638     {
22639         var hours = this.time.getHours();
22640         var minutes = this.time.getMinutes();
22641         var period = 'AM';
22642         
22643         if(hours > 11){
22644             period = 'PM';
22645         }
22646         
22647         if(hours == 0){
22648             hours = 12;
22649         }
22650         
22651         
22652         if(hours > 12){
22653             hours = hours - 12;
22654         }
22655         
22656         if(hours < 10){
22657             hours = '0' + hours;
22658         }
22659         
22660         if(minutes < 10){
22661             minutes = '0' + minutes;
22662         }
22663         
22664         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22665         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22666         this.pop.select('button', true).first().dom.innerHTML = period;
22667         
22668     },
22669     
22670     place: function()
22671     {   
22672         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22673         
22674         var cls = ['bottom'];
22675         
22676         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22677             cls.pop();
22678             cls.push('top');
22679         }
22680         
22681         cls.push('right');
22682         
22683         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22684             cls.pop();
22685             cls.push('left');
22686         }
22687         //this.picker().setXY(20000,20000);
22688         this.picker().addClass(cls.join('-'));
22689         
22690         var _this = this;
22691         
22692         Roo.each(cls, function(c){
22693             if(c == 'bottom'){
22694                 (function() {
22695                  //  
22696                 }).defer(200);
22697                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22698                 //_this.picker().setTop(_this.inputEl().getHeight());
22699                 return;
22700             }
22701             if(c == 'top'){
22702                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22703                 
22704                 //_this.picker().setTop(0 - _this.picker().getHeight());
22705                 return;
22706             }
22707             /*
22708             if(c == 'left'){
22709                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22710                 return;
22711             }
22712             if(c == 'right'){
22713                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22714                 return;
22715             }
22716             */
22717         });
22718         
22719     },
22720   
22721     onFocus : function()
22722     {
22723         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22724         this.show();
22725     },
22726     
22727     onBlur : function()
22728     {
22729         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22730         this.hide();
22731     },
22732     
22733     show : function()
22734     {
22735         this.picker().show();
22736         this.pop.show();
22737         this.update();
22738         this.place();
22739         
22740         this.fireEvent('show', this, this.date);
22741     },
22742     
22743     hide : function()
22744     {
22745         this.picker().hide();
22746         this.pop.hide();
22747         
22748         this.fireEvent('hide', this, this.date);
22749     },
22750     
22751     setTime : function()
22752     {
22753         this.hide();
22754         this.setValue(this.time.format(this.format));
22755         
22756         this.fireEvent('select', this, this.date);
22757         
22758         
22759     },
22760     
22761     onMousedown: function(e){
22762         e.stopPropagation();
22763         e.preventDefault();
22764     },
22765     
22766     onIncrementHours: function()
22767     {
22768         Roo.log('onIncrementHours');
22769         this.time = this.time.add(Date.HOUR, 1);
22770         this.update();
22771         
22772     },
22773     
22774     onDecrementHours: function()
22775     {
22776         Roo.log('onDecrementHours');
22777         this.time = this.time.add(Date.HOUR, -1);
22778         this.update();
22779     },
22780     
22781     onIncrementMinutes: function()
22782     {
22783         Roo.log('onIncrementMinutes');
22784         this.time = this.time.add(Date.MINUTE, 1);
22785         this.update();
22786     },
22787     
22788     onDecrementMinutes: function()
22789     {
22790         Roo.log('onDecrementMinutes');
22791         this.time = this.time.add(Date.MINUTE, -1);
22792         this.update();
22793     },
22794     
22795     onTogglePeriod: function()
22796     {
22797         Roo.log('onTogglePeriod');
22798         this.time = this.time.add(Date.HOUR, 12);
22799         this.update();
22800     }
22801     
22802    
22803 });
22804  
22805
22806 Roo.apply(Roo.bootstrap.TimeField,  {
22807   
22808     template : {
22809         tag: 'div',
22810         cls: 'datepicker dropdown-menu',
22811         cn: [
22812             {
22813                 tag: 'div',
22814                 cls: 'datepicker-time',
22815                 cn: [
22816                 {
22817                     tag: 'table',
22818                     cls: 'table-condensed',
22819                     cn:[
22820                         {
22821                             tag: 'tbody',
22822                             cn: [
22823                                 {
22824                                     tag: 'tr',
22825                                     cn: [
22826                                     {
22827                                         tag: 'td',
22828                                         colspan: '7'
22829                                     }
22830                                     ]
22831                                 }
22832                             ]
22833                         },
22834                         {
22835                             tag: 'tfoot',
22836                             cn: [
22837                                 {
22838                                     tag: 'tr',
22839                                     cn: [
22840                                     {
22841                                         tag: 'th',
22842                                         colspan: '7',
22843                                         cls: '',
22844                                         cn: [
22845                                             {
22846                                                 tag: 'button',
22847                                                 cls: 'btn btn-info ok',
22848                                                 html: 'OK'
22849                                             }
22850                                         ]
22851                                     }
22852                     
22853                                     ]
22854                                 }
22855                             ]
22856                         }
22857                     ]
22858                 }
22859                 ]
22860             }
22861         ]
22862     }
22863 });
22864
22865  
22866
22867  /*
22868  * - LGPL
22869  *
22870  * MonthField
22871  * 
22872  */
22873
22874 /**
22875  * @class Roo.bootstrap.MonthField
22876  * @extends Roo.bootstrap.Input
22877  * Bootstrap MonthField class
22878  * 
22879  * @cfg {String} language default en
22880  * 
22881  * @constructor
22882  * Create a new MonthField
22883  * @param {Object} config The config object
22884  */
22885
22886 Roo.bootstrap.MonthField = function(config){
22887     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22888     
22889     this.addEvents({
22890         /**
22891          * @event show
22892          * Fires when this field show.
22893          * @param {Roo.bootstrap.MonthField} this
22894          * @param {Mixed} date The date value
22895          */
22896         show : true,
22897         /**
22898          * @event show
22899          * Fires when this field hide.
22900          * @param {Roo.bootstrap.MonthField} this
22901          * @param {Mixed} date The date value
22902          */
22903         hide : true,
22904         /**
22905          * @event select
22906          * Fires when select a date.
22907          * @param {Roo.bootstrap.MonthField} this
22908          * @param {String} oldvalue The old value
22909          * @param {String} newvalue The new value
22910          */
22911         select : true
22912     });
22913 };
22914
22915 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22916     
22917     onRender: function(ct, position)
22918     {
22919         
22920         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22921         
22922         this.language = this.language || 'en';
22923         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22924         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22925         
22926         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22927         this.isInline = false;
22928         this.isInput = true;
22929         this.component = this.el.select('.add-on', true).first() || false;
22930         this.component = (this.component && this.component.length === 0) ? false : this.component;
22931         this.hasInput = this.component && this.inputEL().length;
22932         
22933         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22934         
22935         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22936         
22937         this.picker().on('mousedown', this.onMousedown, this);
22938         this.picker().on('click', this.onClick, this);
22939         
22940         this.picker().addClass('datepicker-dropdown');
22941         
22942         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22943             v.setStyle('width', '189px');
22944         });
22945         
22946         this.fillMonths();
22947         
22948         this.update();
22949         
22950         if(this.isInline) {
22951             this.show();
22952         }
22953         
22954     },
22955     
22956     setValue: function(v, suppressEvent)
22957     {   
22958         var o = this.getValue();
22959         
22960         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22961         
22962         this.update();
22963
22964         if(suppressEvent !== true){
22965             this.fireEvent('select', this, o, v);
22966         }
22967         
22968     },
22969     
22970     getValue: function()
22971     {
22972         return this.value;
22973     },
22974     
22975     onClick: function(e) 
22976     {
22977         e.stopPropagation();
22978         e.preventDefault();
22979         
22980         var target = e.getTarget();
22981         
22982         if(target.nodeName.toLowerCase() === 'i'){
22983             target = Roo.get(target).dom.parentNode;
22984         }
22985         
22986         var nodeName = target.nodeName;
22987         var className = target.className;
22988         var html = target.innerHTML;
22989         
22990         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22991             return;
22992         }
22993         
22994         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22995         
22996         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22997         
22998         this.hide();
22999                         
23000     },
23001     
23002     picker : function()
23003     {
23004         return this.pickerEl;
23005     },
23006     
23007     fillMonths: function()
23008     {    
23009         var i = 0;
23010         var months = this.picker().select('>.datepicker-months td', true).first();
23011         
23012         months.dom.innerHTML = '';
23013         
23014         while (i < 12) {
23015             var month = {
23016                 tag: 'span',
23017                 cls: 'month',
23018                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23019             };
23020             
23021             months.createChild(month);
23022         }
23023         
23024     },
23025     
23026     update: function()
23027     {
23028         var _this = this;
23029         
23030         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23031             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23032         }
23033         
23034         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23035             e.removeClass('active');
23036             
23037             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23038                 e.addClass('active');
23039             }
23040         })
23041     },
23042     
23043     place: function()
23044     {
23045         if(this.isInline) {
23046             return;
23047         }
23048         
23049         this.picker().removeClass(['bottom', 'top']);
23050         
23051         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23052             /*
23053              * place to the top of element!
23054              *
23055              */
23056             
23057             this.picker().addClass('top');
23058             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23059             
23060             return;
23061         }
23062         
23063         this.picker().addClass('bottom');
23064         
23065         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23066     },
23067     
23068     onFocus : function()
23069     {
23070         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23071         this.show();
23072     },
23073     
23074     onBlur : function()
23075     {
23076         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23077         
23078         var d = this.inputEl().getValue();
23079         
23080         this.setValue(d);
23081                 
23082         this.hide();
23083     },
23084     
23085     show : function()
23086     {
23087         this.picker().show();
23088         this.picker().select('>.datepicker-months', true).first().show();
23089         this.update();
23090         this.place();
23091         
23092         this.fireEvent('show', this, this.date);
23093     },
23094     
23095     hide : function()
23096     {
23097         if(this.isInline) {
23098             return;
23099         }
23100         this.picker().hide();
23101         this.fireEvent('hide', this, this.date);
23102         
23103     },
23104     
23105     onMousedown: function(e)
23106     {
23107         e.stopPropagation();
23108         e.preventDefault();
23109     },
23110     
23111     keyup: function(e)
23112     {
23113         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23114         this.update();
23115     },
23116
23117     fireKey: function(e)
23118     {
23119         if (!this.picker().isVisible()){
23120             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23121                 this.show();
23122             }
23123             return;
23124         }
23125         
23126         var dir;
23127         
23128         switch(e.keyCode){
23129             case 27: // escape
23130                 this.hide();
23131                 e.preventDefault();
23132                 break;
23133             case 37: // left
23134             case 39: // right
23135                 dir = e.keyCode == 37 ? -1 : 1;
23136                 
23137                 this.vIndex = this.vIndex + dir;
23138                 
23139                 if(this.vIndex < 0){
23140                     this.vIndex = 0;
23141                 }
23142                 
23143                 if(this.vIndex > 11){
23144                     this.vIndex = 11;
23145                 }
23146                 
23147                 if(isNaN(this.vIndex)){
23148                     this.vIndex = 0;
23149                 }
23150                 
23151                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23152                 
23153                 break;
23154             case 38: // up
23155             case 40: // down
23156                 
23157                 dir = e.keyCode == 38 ? -1 : 1;
23158                 
23159                 this.vIndex = this.vIndex + dir * 4;
23160                 
23161                 if(this.vIndex < 0){
23162                     this.vIndex = 0;
23163                 }
23164                 
23165                 if(this.vIndex > 11){
23166                     this.vIndex = 11;
23167                 }
23168                 
23169                 if(isNaN(this.vIndex)){
23170                     this.vIndex = 0;
23171                 }
23172                 
23173                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23174                 break;
23175                 
23176             case 13: // enter
23177                 
23178                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23179                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23180                 }
23181                 
23182                 this.hide();
23183                 e.preventDefault();
23184                 break;
23185             case 9: // tab
23186                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23187                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23188                 }
23189                 this.hide();
23190                 break;
23191             case 16: // shift
23192             case 17: // ctrl
23193             case 18: // alt
23194                 break;
23195             default :
23196                 this.hide();
23197                 
23198         }
23199     },
23200     
23201     remove: function() 
23202     {
23203         this.picker().remove();
23204     }
23205    
23206 });
23207
23208 Roo.apply(Roo.bootstrap.MonthField,  {
23209     
23210     content : {
23211         tag: 'tbody',
23212         cn: [
23213         {
23214             tag: 'tr',
23215             cn: [
23216             {
23217                 tag: 'td',
23218                 colspan: '7'
23219             }
23220             ]
23221         }
23222         ]
23223     },
23224     
23225     dates:{
23226         en: {
23227             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23228             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23229         }
23230     }
23231 });
23232
23233 Roo.apply(Roo.bootstrap.MonthField,  {
23234   
23235     template : {
23236         tag: 'div',
23237         cls: 'datepicker dropdown-menu roo-dynamic',
23238         cn: [
23239             {
23240                 tag: 'div',
23241                 cls: 'datepicker-months',
23242                 cn: [
23243                 {
23244                     tag: 'table',
23245                     cls: 'table-condensed',
23246                     cn:[
23247                         Roo.bootstrap.DateField.content
23248                     ]
23249                 }
23250                 ]
23251             }
23252         ]
23253     }
23254 });
23255
23256  
23257
23258  
23259  /*
23260  * - LGPL
23261  *
23262  * CheckBox
23263  * 
23264  */
23265
23266 /**
23267  * @class Roo.bootstrap.CheckBox
23268  * @extends Roo.bootstrap.Input
23269  * Bootstrap CheckBox class
23270  * 
23271  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23272  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23273  * @cfg {String} boxLabel The text that appears beside the checkbox
23274  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23275  * @cfg {Boolean} checked initnal the element
23276  * @cfg {Boolean} inline inline the element (default false)
23277  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23278  * @cfg {String} tooltip label tooltip
23279  * 
23280  * @constructor
23281  * Create a new CheckBox
23282  * @param {Object} config The config object
23283  */
23284
23285 Roo.bootstrap.CheckBox = function(config){
23286     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23287    
23288     this.addEvents({
23289         /**
23290         * @event check
23291         * Fires when the element is checked or unchecked.
23292         * @param {Roo.bootstrap.CheckBox} this This input
23293         * @param {Boolean} checked The new checked value
23294         */
23295        check : true,
23296        /**
23297         * @event click
23298         * Fires when the element is click.
23299         * @param {Roo.bootstrap.CheckBox} this This input
23300         */
23301        click : true
23302     });
23303     
23304 };
23305
23306 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23307   
23308     inputType: 'checkbox',
23309     inputValue: 1,
23310     valueOff: 0,
23311     boxLabel: false,
23312     checked: false,
23313     weight : false,
23314     inline: false,
23315     tooltip : '',
23316     
23317     // checkbox success does not make any sense really.. 
23318     invalidClass : "",
23319     validClass : "",
23320     
23321     
23322     getAutoCreate : function()
23323     {
23324         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23325         
23326         var id = Roo.id();
23327         
23328         var cfg = {};
23329         
23330         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23331         
23332         if(this.inline){
23333             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23334         }
23335         
23336         var input =  {
23337             tag: 'input',
23338             id : id,
23339             type : this.inputType,
23340             value : this.inputValue,
23341             cls : 'roo-' + this.inputType, //'form-box',
23342             placeholder : this.placeholder || ''
23343             
23344         };
23345         
23346         if(this.inputType != 'radio'){
23347             var hidden =  {
23348                 tag: 'input',
23349                 type : 'hidden',
23350                 cls : 'roo-hidden-value',
23351                 value : this.checked ? this.inputValue : this.valueOff
23352             };
23353         }
23354         
23355             
23356         if (this.weight) { // Validity check?
23357             cfg.cls += " " + this.inputType + "-" + this.weight;
23358         }
23359         
23360         if (this.disabled) {
23361             input.disabled=true;
23362         }
23363         
23364         if(this.checked){
23365             input.checked = this.checked;
23366         }
23367         
23368         if (this.name) {
23369             
23370             input.name = this.name;
23371             
23372             if(this.inputType != 'radio'){
23373                 hidden.name = this.name;
23374                 input.name = '_hidden_' + this.name;
23375             }
23376         }
23377         
23378         if (this.size) {
23379             input.cls += ' input-' + this.size;
23380         }
23381         
23382         var settings=this;
23383         
23384         ['xs','sm','md','lg'].map(function(size){
23385             if (settings[size]) {
23386                 cfg.cls += ' col-' + size + '-' + settings[size];
23387             }
23388         });
23389         
23390         var inputblock = input;
23391          
23392         if (this.before || this.after) {
23393             
23394             inputblock = {
23395                 cls : 'input-group',
23396                 cn :  [] 
23397             };
23398             
23399             if (this.before) {
23400                 inputblock.cn.push({
23401                     tag :'span',
23402                     cls : 'input-group-addon',
23403                     html : this.before
23404                 });
23405             }
23406             
23407             inputblock.cn.push(input);
23408             
23409             if(this.inputType != 'radio'){
23410                 inputblock.cn.push(hidden);
23411             }
23412             
23413             if (this.after) {
23414                 inputblock.cn.push({
23415                     tag :'span',
23416                     cls : 'input-group-addon',
23417                     html : this.after
23418                 });
23419             }
23420             
23421         }
23422         var boxLabelCfg = false;
23423         
23424         if(this.boxLabel){
23425            
23426             boxLabelCfg = {
23427                 tag: 'label',
23428                 //'for': id, // box label is handled by onclick - so no for...
23429                 cls: 'box-label',
23430                 html: this.boxLabel
23431             };
23432             if(this.tooltip){
23433                 boxLabelCfg.tooltip = this.tooltip;
23434             }
23435              
23436         }
23437         
23438         
23439         if (align ==='left' && this.fieldLabel.length) {
23440 //                Roo.log("left and has label");
23441             cfg.cn = [
23442                 {
23443                     tag: 'label',
23444                     'for' :  id,
23445                     cls : 'control-label',
23446                     html : this.fieldLabel
23447                 },
23448                 {
23449                     cls : "", 
23450                     cn: [
23451                         inputblock
23452                     ]
23453                 }
23454             ];
23455             
23456             if (boxLabelCfg) {
23457                 cfg.cn[1].cn.push(boxLabelCfg);
23458             }
23459             
23460             if(this.labelWidth > 12){
23461                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23462             }
23463             
23464             if(this.labelWidth < 13 && this.labelmd == 0){
23465                 this.labelmd = this.labelWidth;
23466             }
23467             
23468             if(this.labellg > 0){
23469                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23470                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23471             }
23472             
23473             if(this.labelmd > 0){
23474                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23475                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23476             }
23477             
23478             if(this.labelsm > 0){
23479                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23480                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23481             }
23482             
23483             if(this.labelxs > 0){
23484                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23485                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23486             }
23487             
23488         } else if ( this.fieldLabel.length) {
23489 //                Roo.log(" label");
23490                 cfg.cn = [
23491                    
23492                     {
23493                         tag: this.boxLabel ? 'span' : 'label',
23494                         'for': id,
23495                         cls: 'control-label box-input-label',
23496                         //cls : 'input-group-addon',
23497                         html : this.fieldLabel
23498                     },
23499                     
23500                     inputblock
23501                     
23502                 ];
23503                 if (boxLabelCfg) {
23504                     cfg.cn.push(boxLabelCfg);
23505                 }
23506
23507         } else {
23508             
23509 //                Roo.log(" no label && no align");
23510                 cfg.cn = [  inputblock ] ;
23511                 if (boxLabelCfg) {
23512                     cfg.cn.push(boxLabelCfg);
23513                 }
23514
23515                 
23516         }
23517         
23518        
23519         
23520         if(this.inputType != 'radio'){
23521             cfg.cn.push(hidden);
23522         }
23523         
23524         return cfg;
23525         
23526     },
23527     
23528     /**
23529      * return the real input element.
23530      */
23531     inputEl: function ()
23532     {
23533         return this.el.select('input.roo-' + this.inputType,true).first();
23534     },
23535     hiddenEl: function ()
23536     {
23537         return this.el.select('input.roo-hidden-value',true).first();
23538     },
23539     
23540     labelEl: function()
23541     {
23542         return this.el.select('label.control-label',true).first();
23543     },
23544     /* depricated... */
23545     
23546     label: function()
23547     {
23548         return this.labelEl();
23549     },
23550     
23551     boxLabelEl: function()
23552     {
23553         return this.el.select('label.box-label',true).first();
23554     },
23555     
23556     initEvents : function()
23557     {
23558 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23559         
23560         this.inputEl().on('click', this.onClick,  this);
23561         
23562         if (this.boxLabel) { 
23563             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23564         }
23565         
23566         this.startValue = this.getValue();
23567         
23568         if(this.groupId){
23569             Roo.bootstrap.CheckBox.register(this);
23570         }
23571     },
23572     
23573     onClick : function(e)
23574     {   
23575         if(this.fireEvent('click', this, e) !== false){
23576             this.setChecked(!this.checked);
23577         }
23578         
23579     },
23580     
23581     setChecked : function(state,suppressEvent)
23582     {
23583         this.startValue = this.getValue();
23584
23585         if(this.inputType == 'radio'){
23586             
23587             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23588                 e.dom.checked = false;
23589             });
23590             
23591             this.inputEl().dom.checked = true;
23592             
23593             this.inputEl().dom.value = this.inputValue;
23594             
23595             if(suppressEvent !== true){
23596                 this.fireEvent('check', this, true);
23597             }
23598             
23599             this.validate();
23600             
23601             return;
23602         }
23603         
23604         this.checked = state;
23605         
23606         this.inputEl().dom.checked = state;
23607         
23608         
23609         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23610         
23611         if(suppressEvent !== true){
23612             this.fireEvent('check', this, state);
23613         }
23614         
23615         this.validate();
23616     },
23617     
23618     getValue : function()
23619     {
23620         if(this.inputType == 'radio'){
23621             return this.getGroupValue();
23622         }
23623         
23624         return this.hiddenEl().dom.value;
23625         
23626     },
23627     
23628     getGroupValue : function()
23629     {
23630         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23631             return '';
23632         }
23633         
23634         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23635     },
23636     
23637     setValue : function(v,suppressEvent)
23638     {
23639         if(this.inputType == 'radio'){
23640             this.setGroupValue(v, suppressEvent);
23641             return;
23642         }
23643         
23644         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23645         
23646         this.validate();
23647     },
23648     
23649     setGroupValue : function(v, suppressEvent)
23650     {
23651         this.startValue = this.getValue();
23652         
23653         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23654             e.dom.checked = false;
23655             
23656             if(e.dom.value == v){
23657                 e.dom.checked = true;
23658             }
23659         });
23660         
23661         if(suppressEvent !== true){
23662             this.fireEvent('check', this, true);
23663         }
23664
23665         this.validate();
23666         
23667         return;
23668     },
23669     
23670     validate : function()
23671     {
23672         if(this.getVisibilityEl().hasClass('hidden')){
23673             return true;
23674         }
23675         
23676         if(
23677                 this.disabled || 
23678                 (this.inputType == 'radio' && this.validateRadio()) ||
23679                 (this.inputType == 'checkbox' && this.validateCheckbox())
23680         ){
23681             this.markValid();
23682             return true;
23683         }
23684         
23685         this.markInvalid();
23686         return false;
23687     },
23688     
23689     validateRadio : function()
23690     {
23691         if(this.getVisibilityEl().hasClass('hidden')){
23692             return true;
23693         }
23694         
23695         if(this.allowBlank){
23696             return true;
23697         }
23698         
23699         var valid = false;
23700         
23701         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23702             if(!e.dom.checked){
23703                 return;
23704             }
23705             
23706             valid = true;
23707             
23708             return false;
23709         });
23710         
23711         return valid;
23712     },
23713     
23714     validateCheckbox : function()
23715     {
23716         if(!this.groupId){
23717             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23718             //return (this.getValue() == this.inputValue) ? true : false;
23719         }
23720         
23721         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23722         
23723         if(!group){
23724             return false;
23725         }
23726         
23727         var r = false;
23728         
23729         for(var i in group){
23730             if(group[i].el.isVisible(true)){
23731                 r = false;
23732                 break;
23733             }
23734             
23735             r = true;
23736         }
23737         
23738         for(var i in group){
23739             if(r){
23740                 break;
23741             }
23742             
23743             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23744         }
23745         
23746         return r;
23747     },
23748     
23749     /**
23750      * Mark this field as valid
23751      */
23752     markValid : function()
23753     {
23754         var _this = this;
23755         
23756         this.fireEvent('valid', this);
23757         
23758         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23759         
23760         if(this.groupId){
23761             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23762         }
23763         
23764         if(label){
23765             label.markValid();
23766         }
23767
23768         if(this.inputType == 'radio'){
23769             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23770                 var fg = e.findParent('.form-group', false, true);
23771                 if (Roo.bootstrap.version == 3) {
23772                     fg.removeClass([_this.invalidClass, _this.validClass]);
23773                     fg.addClass(_this.validClass);
23774                 } else {
23775                     fg.removeClass(['is-valid', 'is-invalid']);
23776                     fg.addClass('is-valid');
23777                 }
23778             });
23779             
23780             return;
23781         }
23782
23783         if(!this.groupId){
23784             var fg = this.el.findParent('.form-group', false, true);
23785             if (Roo.bootstrap.version == 3) {
23786                 fg.removeClass([this.invalidClass, this.validClass]);
23787                 fg.addClass(this.validClass);
23788             } else {
23789                 fg.removeClass(['is-valid', 'is-invalid']);
23790                 fg.addClass('is-valid');
23791             }
23792             return;
23793         }
23794         
23795         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23796         
23797         if(!group){
23798             return;
23799         }
23800         
23801         for(var i in group){
23802             var fg = group[i].el.findParent('.form-group', false, true);
23803             if (Roo.bootstrap.version == 3) {
23804                 fg.removeClass([this.invalidClass, this.validClass]);
23805                 fg.addClass(this.validClass);
23806             } else {
23807                 fg.removeClass(['is-valid', 'is-invalid']);
23808                 fg.addClass('is-valid');
23809             }
23810         }
23811     },
23812     
23813      /**
23814      * Mark this field as invalid
23815      * @param {String} msg The validation message
23816      */
23817     markInvalid : function(msg)
23818     {
23819         if(this.allowBlank){
23820             return;
23821         }
23822         
23823         var _this = this;
23824         
23825         this.fireEvent('invalid', this, msg);
23826         
23827         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23828         
23829         if(this.groupId){
23830             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23831         }
23832         
23833         if(label){
23834             label.markInvalid();
23835         }
23836             
23837         if(this.inputType == 'radio'){
23838             
23839             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23840                 var fg = e.findParent('.form-group', false, true);
23841                 if (Roo.bootstrap.version == 3) {
23842                     fg.removeClass([_this.invalidClass, _this.validClass]);
23843                     fg.addClass(_this.invalidClass);
23844                 } else {
23845                     fg.removeClass(['is-invalid', 'is-valid']);
23846                     fg.addClass('is-invalid');
23847                 }
23848             });
23849             
23850             return;
23851         }
23852         
23853         if(!this.groupId){
23854             var fg = this.el.findParent('.form-group', false, true);
23855             if (Roo.bootstrap.version == 3) {
23856                 fg.removeClass([_this.invalidClass, _this.validClass]);
23857                 fg.addClass(_this.invalidClass);
23858             } else {
23859                 fg.removeClass(['is-invalid', 'is-valid']);
23860                 fg.addClass('is-invalid');
23861             }
23862             return;
23863         }
23864         
23865         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23866         
23867         if(!group){
23868             return;
23869         }
23870         
23871         for(var i in group){
23872             var fg = group[i].el.findParent('.form-group', false, true);
23873             if (Roo.bootstrap.version == 3) {
23874                 fg.removeClass([_this.invalidClass, _this.validClass]);
23875                 fg.addClass(_this.invalidClass);
23876             } else {
23877                 fg.removeClass(['is-invalid', 'is-valid']);
23878                 fg.addClass('is-invalid');
23879             }
23880         }
23881         
23882     },
23883     
23884     clearInvalid : function()
23885     {
23886         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23887         
23888         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23889         
23890         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23891         
23892         if (label && label.iconEl) {
23893             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23894             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23895         }
23896     },
23897     
23898     disable : function()
23899     {
23900         if(this.inputType != 'radio'){
23901             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23902             return;
23903         }
23904         
23905         var _this = this;
23906         
23907         if(this.rendered){
23908             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23909                 _this.getActionEl().addClass(this.disabledClass);
23910                 e.dom.disabled = true;
23911             });
23912         }
23913         
23914         this.disabled = true;
23915         this.fireEvent("disable", this);
23916         return this;
23917     },
23918
23919     enable : function()
23920     {
23921         if(this.inputType != 'radio'){
23922             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23923             return;
23924         }
23925         
23926         var _this = this;
23927         
23928         if(this.rendered){
23929             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23930                 _this.getActionEl().removeClass(this.disabledClass);
23931                 e.dom.disabled = false;
23932             });
23933         }
23934         
23935         this.disabled = false;
23936         this.fireEvent("enable", this);
23937         return this;
23938     },
23939     
23940     setBoxLabel : function(v)
23941     {
23942         this.boxLabel = v;
23943         
23944         if(this.rendered){
23945             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23946         }
23947     }
23948
23949 });
23950
23951 Roo.apply(Roo.bootstrap.CheckBox, {
23952     
23953     groups: {},
23954     
23955      /**
23956     * register a CheckBox Group
23957     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23958     */
23959     register : function(checkbox)
23960     {
23961         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23962             this.groups[checkbox.groupId] = {};
23963         }
23964         
23965         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23966             return;
23967         }
23968         
23969         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23970         
23971     },
23972     /**
23973     * fetch a CheckBox Group based on the group ID
23974     * @param {string} the group ID
23975     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23976     */
23977     get: function(groupId) {
23978         if (typeof(this.groups[groupId]) == 'undefined') {
23979             return false;
23980         }
23981         
23982         return this.groups[groupId] ;
23983     }
23984     
23985     
23986 });
23987 /*
23988  * - LGPL
23989  *
23990  * RadioItem
23991  * 
23992  */
23993
23994 /**
23995  * @class Roo.bootstrap.Radio
23996  * @extends Roo.bootstrap.Component
23997  * Bootstrap Radio class
23998  * @cfg {String} boxLabel - the label associated
23999  * @cfg {String} value - the value of radio
24000  * 
24001  * @constructor
24002  * Create a new Radio
24003  * @param {Object} config The config object
24004  */
24005 Roo.bootstrap.Radio = function(config){
24006     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24007     
24008 };
24009
24010 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24011     
24012     boxLabel : '',
24013     
24014     value : '',
24015     
24016     getAutoCreate : function()
24017     {
24018         var cfg = {
24019             tag : 'div',
24020             cls : 'form-group radio',
24021             cn : [
24022                 {
24023                     tag : 'label',
24024                     cls : 'box-label',
24025                     html : this.boxLabel
24026                 }
24027             ]
24028         };
24029         
24030         return cfg;
24031     },
24032     
24033     initEvents : function() 
24034     {
24035         this.parent().register(this);
24036         
24037         this.el.on('click', this.onClick, this);
24038         
24039     },
24040     
24041     onClick : function(e)
24042     {
24043         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24044             this.setChecked(true);
24045         }
24046     },
24047     
24048     setChecked : function(state, suppressEvent)
24049     {
24050         this.parent().setValue(this.value, suppressEvent);
24051         
24052     },
24053     
24054     setBoxLabel : function(v)
24055     {
24056         this.boxLabel = v;
24057         
24058         if(this.rendered){
24059             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24060         }
24061     }
24062     
24063 });
24064  
24065
24066  /*
24067  * - LGPL
24068  *
24069  * Input
24070  * 
24071  */
24072
24073 /**
24074  * @class Roo.bootstrap.SecurePass
24075  * @extends Roo.bootstrap.Input
24076  * Bootstrap SecurePass class
24077  *
24078  * 
24079  * @constructor
24080  * Create a new SecurePass
24081  * @param {Object} config The config object
24082  */
24083  
24084 Roo.bootstrap.SecurePass = function (config) {
24085     // these go here, so the translation tool can replace them..
24086     this.errors = {
24087         PwdEmpty: "Please type a password, and then retype it to confirm.",
24088         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24089         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24090         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24091         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24092         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24093         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24094         TooWeak: "Your password is Too Weak."
24095     },
24096     this.meterLabel = "Password strength:";
24097     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24098     this.meterClass = [
24099         "roo-password-meter-tooweak", 
24100         "roo-password-meter-weak", 
24101         "roo-password-meter-medium", 
24102         "roo-password-meter-strong", 
24103         "roo-password-meter-grey"
24104     ];
24105     
24106     this.errors = {};
24107     
24108     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24109 }
24110
24111 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24112     /**
24113      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24114      * {
24115      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24116      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24117      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24118      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24119      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24120      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24121      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24122      * })
24123      */
24124     // private
24125     
24126     meterWidth: 300,
24127     errorMsg :'',    
24128     errors: false,
24129     imageRoot: '/',
24130     /**
24131      * @cfg {String/Object} Label for the strength meter (defaults to
24132      * 'Password strength:')
24133      */
24134     // private
24135     meterLabel: '',
24136     /**
24137      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24138      * ['Weak', 'Medium', 'Strong'])
24139      */
24140     // private    
24141     pwdStrengths: false,    
24142     // private
24143     strength: 0,
24144     // private
24145     _lastPwd: null,
24146     // private
24147     kCapitalLetter: 0,
24148     kSmallLetter: 1,
24149     kDigit: 2,
24150     kPunctuation: 3,
24151     
24152     insecure: false,
24153     // private
24154     initEvents: function ()
24155     {
24156         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24157
24158         if (this.el.is('input[type=password]') && Roo.isSafari) {
24159             this.el.on('keydown', this.SafariOnKeyDown, this);
24160         }
24161
24162         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24163     },
24164     // private
24165     onRender: function (ct, position)
24166     {
24167         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24168         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24169         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24170
24171         this.trigger.createChild({
24172                    cn: [
24173                     {
24174                     //id: 'PwdMeter',
24175                     tag: 'div',
24176                     cls: 'roo-password-meter-grey col-xs-12',
24177                     style: {
24178                         //width: 0,
24179                         //width: this.meterWidth + 'px'                                                
24180                         }
24181                     },
24182                     {                            
24183                          cls: 'roo-password-meter-text'                          
24184                     }
24185                 ]            
24186         });
24187
24188          
24189         if (this.hideTrigger) {
24190             this.trigger.setDisplayed(false);
24191         }
24192         this.setSize(this.width || '', this.height || '');
24193     },
24194     // private
24195     onDestroy: function ()
24196     {
24197         if (this.trigger) {
24198             this.trigger.removeAllListeners();
24199             this.trigger.remove();
24200         }
24201         if (this.wrap) {
24202             this.wrap.remove();
24203         }
24204         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24205     },
24206     // private
24207     checkStrength: function ()
24208     {
24209         var pwd = this.inputEl().getValue();
24210         if (pwd == this._lastPwd) {
24211             return;
24212         }
24213
24214         var strength;
24215         if (this.ClientSideStrongPassword(pwd)) {
24216             strength = 3;
24217         } else if (this.ClientSideMediumPassword(pwd)) {
24218             strength = 2;
24219         } else if (this.ClientSideWeakPassword(pwd)) {
24220             strength = 1;
24221         } else {
24222             strength = 0;
24223         }
24224         
24225         Roo.log('strength1: ' + strength);
24226         
24227         //var pm = this.trigger.child('div/div/div').dom;
24228         var pm = this.trigger.child('div/div');
24229         pm.removeClass(this.meterClass);
24230         pm.addClass(this.meterClass[strength]);
24231                 
24232         
24233         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24234                 
24235         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24236         
24237         this._lastPwd = pwd;
24238     },
24239     reset: function ()
24240     {
24241         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24242         
24243         this._lastPwd = '';
24244         
24245         var pm = this.trigger.child('div/div');
24246         pm.removeClass(this.meterClass);
24247         pm.addClass('roo-password-meter-grey');        
24248         
24249         
24250         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24251         
24252         pt.innerHTML = '';
24253         this.inputEl().dom.type='password';
24254     },
24255     // private
24256     validateValue: function (value)
24257     {
24258         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24259             return false;
24260         }
24261         if (value.length == 0) {
24262             if (this.allowBlank) {
24263                 this.clearInvalid();
24264                 return true;
24265             }
24266
24267             this.markInvalid(this.errors.PwdEmpty);
24268             this.errorMsg = this.errors.PwdEmpty;
24269             return false;
24270         }
24271         
24272         if(this.insecure){
24273             return true;
24274         }
24275         
24276         if (!value.match(/[\x21-\x7e]+/)) {
24277             this.markInvalid(this.errors.PwdBadChar);
24278             this.errorMsg = this.errors.PwdBadChar;
24279             return false;
24280         }
24281         if (value.length < 6) {
24282             this.markInvalid(this.errors.PwdShort);
24283             this.errorMsg = this.errors.PwdShort;
24284             return false;
24285         }
24286         if (value.length > 16) {
24287             this.markInvalid(this.errors.PwdLong);
24288             this.errorMsg = this.errors.PwdLong;
24289             return false;
24290         }
24291         var strength;
24292         if (this.ClientSideStrongPassword(value)) {
24293             strength = 3;
24294         } else if (this.ClientSideMediumPassword(value)) {
24295             strength = 2;
24296         } else if (this.ClientSideWeakPassword(value)) {
24297             strength = 1;
24298         } else {
24299             strength = 0;
24300         }
24301
24302         
24303         if (strength < 2) {
24304             //this.markInvalid(this.errors.TooWeak);
24305             this.errorMsg = this.errors.TooWeak;
24306             //return false;
24307         }
24308         
24309         
24310         console.log('strength2: ' + strength);
24311         
24312         //var pm = this.trigger.child('div/div/div').dom;
24313         
24314         var pm = this.trigger.child('div/div');
24315         pm.removeClass(this.meterClass);
24316         pm.addClass(this.meterClass[strength]);
24317                 
24318         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24319                 
24320         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24321         
24322         this.errorMsg = ''; 
24323         return true;
24324     },
24325     // private
24326     CharacterSetChecks: function (type)
24327     {
24328         this.type = type;
24329         this.fResult = false;
24330     },
24331     // private
24332     isctype: function (character, type)
24333     {
24334         switch (type) {  
24335             case this.kCapitalLetter:
24336                 if (character >= 'A' && character <= 'Z') {
24337                     return true;
24338                 }
24339                 break;
24340             
24341             case this.kSmallLetter:
24342                 if (character >= 'a' && character <= 'z') {
24343                     return true;
24344                 }
24345                 break;
24346             
24347             case this.kDigit:
24348                 if (character >= '0' && character <= '9') {
24349                     return true;
24350                 }
24351                 break;
24352             
24353             case this.kPunctuation:
24354                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24355                     return true;
24356                 }
24357                 break;
24358             
24359             default:
24360                 return false;
24361         }
24362
24363     },
24364     // private
24365     IsLongEnough: function (pwd, size)
24366     {
24367         return !(pwd == null || isNaN(size) || pwd.length < size);
24368     },
24369     // private
24370     SpansEnoughCharacterSets: function (word, nb)
24371     {
24372         if (!this.IsLongEnough(word, nb))
24373         {
24374             return false;
24375         }
24376
24377         var characterSetChecks = new Array(
24378             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24379             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24380         );
24381         
24382         for (var index = 0; index < word.length; ++index) {
24383             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24384                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24385                     characterSetChecks[nCharSet].fResult = true;
24386                     break;
24387                 }
24388             }
24389         }
24390
24391         var nCharSets = 0;
24392         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24393             if (characterSetChecks[nCharSet].fResult) {
24394                 ++nCharSets;
24395             }
24396         }
24397
24398         if (nCharSets < nb) {
24399             return false;
24400         }
24401         return true;
24402     },
24403     // private
24404     ClientSideStrongPassword: function (pwd)
24405     {
24406         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24407     },
24408     // private
24409     ClientSideMediumPassword: function (pwd)
24410     {
24411         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24412     },
24413     // private
24414     ClientSideWeakPassword: function (pwd)
24415     {
24416         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24417     }
24418           
24419 })//<script type="text/javascript">
24420
24421 /*
24422  * Based  Ext JS Library 1.1.1
24423  * Copyright(c) 2006-2007, Ext JS, LLC.
24424  * LGPL
24425  *
24426  */
24427  
24428 /**
24429  * @class Roo.HtmlEditorCore
24430  * @extends Roo.Component
24431  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24432  *
24433  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24434  */
24435
24436 Roo.HtmlEditorCore = function(config){
24437     
24438     
24439     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24440     
24441     
24442     this.addEvents({
24443         /**
24444          * @event initialize
24445          * Fires when the editor is fully initialized (including the iframe)
24446          * @param {Roo.HtmlEditorCore} this
24447          */
24448         initialize: true,
24449         /**
24450          * @event activate
24451          * Fires when the editor is first receives the focus. Any insertion must wait
24452          * until after this event.
24453          * @param {Roo.HtmlEditorCore} this
24454          */
24455         activate: true,
24456          /**
24457          * @event beforesync
24458          * Fires before the textarea is updated with content from the editor iframe. Return false
24459          * to cancel the sync.
24460          * @param {Roo.HtmlEditorCore} this
24461          * @param {String} html
24462          */
24463         beforesync: true,
24464          /**
24465          * @event beforepush
24466          * Fires before the iframe editor is updated with content from the textarea. Return false
24467          * to cancel the push.
24468          * @param {Roo.HtmlEditorCore} this
24469          * @param {String} html
24470          */
24471         beforepush: true,
24472          /**
24473          * @event sync
24474          * Fires when the textarea is updated with content from the editor iframe.
24475          * @param {Roo.HtmlEditorCore} this
24476          * @param {String} html
24477          */
24478         sync: true,
24479          /**
24480          * @event push
24481          * Fires when the iframe editor is updated with content from the textarea.
24482          * @param {Roo.HtmlEditorCore} this
24483          * @param {String} html
24484          */
24485         push: true,
24486         
24487         /**
24488          * @event editorevent
24489          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24490          * @param {Roo.HtmlEditorCore} this
24491          */
24492         editorevent: true
24493         
24494     });
24495     
24496     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24497     
24498     // defaults : white / black...
24499     this.applyBlacklists();
24500     
24501     
24502     
24503 };
24504
24505
24506 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24507
24508
24509      /**
24510      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24511      */
24512     
24513     owner : false,
24514     
24515      /**
24516      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24517      *                        Roo.resizable.
24518      */
24519     resizable : false,
24520      /**
24521      * @cfg {Number} height (in pixels)
24522      */   
24523     height: 300,
24524    /**
24525      * @cfg {Number} width (in pixels)
24526      */   
24527     width: 500,
24528     
24529     /**
24530      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24531      * 
24532      */
24533     stylesheets: false,
24534     
24535     // id of frame..
24536     frameId: false,
24537     
24538     // private properties
24539     validationEvent : false,
24540     deferHeight: true,
24541     initialized : false,
24542     activated : false,
24543     sourceEditMode : false,
24544     onFocus : Roo.emptyFn,
24545     iframePad:3,
24546     hideMode:'offsets',
24547     
24548     clearUp: true,
24549     
24550     // blacklist + whitelisted elements..
24551     black: false,
24552     white: false,
24553      
24554     bodyCls : '',
24555
24556     /**
24557      * Protected method that will not generally be called directly. It
24558      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24559      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24560      */
24561     getDocMarkup : function(){
24562         // body styles..
24563         var st = '';
24564         
24565         // inherit styels from page...?? 
24566         if (this.stylesheets === false) {
24567             
24568             Roo.get(document.head).select('style').each(function(node) {
24569                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24570             });
24571             
24572             Roo.get(document.head).select('link').each(function(node) { 
24573                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24574             });
24575             
24576         } else if (!this.stylesheets.length) {
24577                 // simple..
24578                 st = '<style type="text/css">' +
24579                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24580                    '</style>';
24581         } else {
24582             for (var i in this.stylesheets) { 
24583                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24584             }
24585             
24586         }
24587         
24588         st +=  '<style type="text/css">' +
24589             'IMG { cursor: pointer } ' +
24590         '</style>';
24591
24592         var cls = 'roo-htmleditor-body';
24593         
24594         if(this.bodyCls.length){
24595             cls += ' ' + this.bodyCls;
24596         }
24597         
24598         return '<html><head>' + st  +
24599             //<style type="text/css">' +
24600             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24601             //'</style>' +
24602             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24603     },
24604
24605     // private
24606     onRender : function(ct, position)
24607     {
24608         var _t = this;
24609         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24610         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24611         
24612         
24613         this.el.dom.style.border = '0 none';
24614         this.el.dom.setAttribute('tabIndex', -1);
24615         this.el.addClass('x-hidden hide');
24616         
24617         
24618         
24619         if(Roo.isIE){ // fix IE 1px bogus margin
24620             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24621         }
24622        
24623         
24624         this.frameId = Roo.id();
24625         
24626          
24627         
24628         var iframe = this.owner.wrap.createChild({
24629             tag: 'iframe',
24630             cls: 'form-control', // bootstrap..
24631             id: this.frameId,
24632             name: this.frameId,
24633             frameBorder : 'no',
24634             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24635         }, this.el
24636         );
24637         
24638         
24639         this.iframe = iframe.dom;
24640
24641          this.assignDocWin();
24642         
24643         this.doc.designMode = 'on';
24644        
24645         this.doc.open();
24646         this.doc.write(this.getDocMarkup());
24647         this.doc.close();
24648
24649         
24650         var task = { // must defer to wait for browser to be ready
24651             run : function(){
24652                 //console.log("run task?" + this.doc.readyState);
24653                 this.assignDocWin();
24654                 if(this.doc.body || this.doc.readyState == 'complete'){
24655                     try {
24656                         this.doc.designMode="on";
24657                     } catch (e) {
24658                         return;
24659                     }
24660                     Roo.TaskMgr.stop(task);
24661                     this.initEditor.defer(10, this);
24662                 }
24663             },
24664             interval : 10,
24665             duration: 10000,
24666             scope: this
24667         };
24668         Roo.TaskMgr.start(task);
24669
24670     },
24671
24672     // private
24673     onResize : function(w, h)
24674     {
24675          Roo.log('resize: ' +w + ',' + h );
24676         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24677         if(!this.iframe){
24678             return;
24679         }
24680         if(typeof w == 'number'){
24681             
24682             this.iframe.style.width = w + 'px';
24683         }
24684         if(typeof h == 'number'){
24685             
24686             this.iframe.style.height = h + 'px';
24687             if(this.doc){
24688                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24689             }
24690         }
24691         
24692     },
24693
24694     /**
24695      * Toggles the editor between standard and source edit mode.
24696      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24697      */
24698     toggleSourceEdit : function(sourceEditMode){
24699         
24700         this.sourceEditMode = sourceEditMode === true;
24701         
24702         if(this.sourceEditMode){
24703  
24704             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24705             
24706         }else{
24707             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24708             //this.iframe.className = '';
24709             this.deferFocus();
24710         }
24711         //this.setSize(this.owner.wrap.getSize());
24712         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24713     },
24714
24715     
24716   
24717
24718     /**
24719      * Protected method that will not generally be called directly. If you need/want
24720      * custom HTML cleanup, this is the method you should override.
24721      * @param {String} html The HTML to be cleaned
24722      * return {String} The cleaned HTML
24723      */
24724     cleanHtml : function(html){
24725         html = String(html);
24726         if(html.length > 5){
24727             if(Roo.isSafari){ // strip safari nonsense
24728                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24729             }
24730         }
24731         if(html == '&nbsp;'){
24732             html = '';
24733         }
24734         return html;
24735     },
24736
24737     /**
24738      * HTML Editor -> Textarea
24739      * Protected method that will not generally be called directly. Syncs the contents
24740      * of the editor iframe with the textarea.
24741      */
24742     syncValue : function(){
24743         if(this.initialized){
24744             var bd = (this.doc.body || this.doc.documentElement);
24745             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24746             var html = bd.innerHTML;
24747             if(Roo.isSafari){
24748                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24749                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24750                 if(m && m[1]){
24751                     html = '<div style="'+m[0]+'">' + html + '</div>';
24752                 }
24753             }
24754             html = this.cleanHtml(html);
24755             // fix up the special chars.. normaly like back quotes in word...
24756             // however we do not want to do this with chinese..
24757             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24758                 
24759                 var cc = match.charCodeAt();
24760
24761                 // Get the character value, handling surrogate pairs
24762                 if (match.length == 2) {
24763                     // It's a surrogate pair, calculate the Unicode code point
24764                     var high = match.charCodeAt(0) - 0xD800;
24765                     var low  = match.charCodeAt(1) - 0xDC00;
24766                     cc = (high * 0x400) + low + 0x10000;
24767                 }  else if (
24768                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24769                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24770                     (cc >= 0xf900 && cc < 0xfb00 )
24771                 ) {
24772                         return match;
24773                 }  
24774          
24775                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24776                 return "&#" + cc + ";";
24777                 
24778                 
24779             });
24780             
24781             
24782              
24783             if(this.owner.fireEvent('beforesync', this, html) !== false){
24784                 this.el.dom.value = html;
24785                 this.owner.fireEvent('sync', this, html);
24786             }
24787         }
24788     },
24789
24790     /**
24791      * Protected method that will not generally be called directly. Pushes the value of the textarea
24792      * into the iframe editor.
24793      */
24794     pushValue : function(){
24795         if(this.initialized){
24796             var v = this.el.dom.value.trim();
24797             
24798 //            if(v.length < 1){
24799 //                v = '&#160;';
24800 //            }
24801             
24802             if(this.owner.fireEvent('beforepush', this, v) !== false){
24803                 var d = (this.doc.body || this.doc.documentElement);
24804                 d.innerHTML = v;
24805                 this.cleanUpPaste();
24806                 this.el.dom.value = d.innerHTML;
24807                 this.owner.fireEvent('push', this, v);
24808             }
24809         }
24810     },
24811
24812     // private
24813     deferFocus : function(){
24814         this.focus.defer(10, this);
24815     },
24816
24817     // doc'ed in Field
24818     focus : function(){
24819         if(this.win && !this.sourceEditMode){
24820             this.win.focus();
24821         }else{
24822             this.el.focus();
24823         }
24824     },
24825     
24826     assignDocWin: function()
24827     {
24828         var iframe = this.iframe;
24829         
24830          if(Roo.isIE){
24831             this.doc = iframe.contentWindow.document;
24832             this.win = iframe.contentWindow;
24833         } else {
24834 //            if (!Roo.get(this.frameId)) {
24835 //                return;
24836 //            }
24837 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24838 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24839             
24840             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24841                 return;
24842             }
24843             
24844             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24845             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24846         }
24847     },
24848     
24849     // private
24850     initEditor : function(){
24851         //console.log("INIT EDITOR");
24852         this.assignDocWin();
24853         
24854         
24855         
24856         this.doc.designMode="on";
24857         this.doc.open();
24858         this.doc.write(this.getDocMarkup());
24859         this.doc.close();
24860         
24861         var dbody = (this.doc.body || this.doc.documentElement);
24862         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24863         // this copies styles from the containing element into thsi one..
24864         // not sure why we need all of this..
24865         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24866         
24867         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24868         //ss['background-attachment'] = 'fixed'; // w3c
24869         dbody.bgProperties = 'fixed'; // ie
24870         //Roo.DomHelper.applyStyles(dbody, ss);
24871         Roo.EventManager.on(this.doc, {
24872             //'mousedown': this.onEditorEvent,
24873             'mouseup': this.onEditorEvent,
24874             'dblclick': this.onEditorEvent,
24875             'click': this.onEditorEvent,
24876             'keyup': this.onEditorEvent,
24877             buffer:100,
24878             scope: this
24879         });
24880         if(Roo.isGecko){
24881             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24882         }
24883         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24884             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24885         }
24886         this.initialized = true;
24887
24888         this.owner.fireEvent('initialize', this);
24889         this.pushValue();
24890     },
24891
24892     // private
24893     onDestroy : function(){
24894         
24895         
24896         
24897         if(this.rendered){
24898             
24899             //for (var i =0; i < this.toolbars.length;i++) {
24900             //    // fixme - ask toolbars for heights?
24901             //    this.toolbars[i].onDestroy();
24902            // }
24903             
24904             //this.wrap.dom.innerHTML = '';
24905             //this.wrap.remove();
24906         }
24907     },
24908
24909     // private
24910     onFirstFocus : function(){
24911         
24912         this.assignDocWin();
24913         
24914         
24915         this.activated = true;
24916          
24917     
24918         if(Roo.isGecko){ // prevent silly gecko errors
24919             this.win.focus();
24920             var s = this.win.getSelection();
24921             if(!s.focusNode || s.focusNode.nodeType != 3){
24922                 var r = s.getRangeAt(0);
24923                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24924                 r.collapse(true);
24925                 this.deferFocus();
24926             }
24927             try{
24928                 this.execCmd('useCSS', true);
24929                 this.execCmd('styleWithCSS', false);
24930             }catch(e){}
24931         }
24932         this.owner.fireEvent('activate', this);
24933     },
24934
24935     // private
24936     adjustFont: function(btn){
24937         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24938         //if(Roo.isSafari){ // safari
24939         //    adjust *= 2;
24940        // }
24941         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24942         if(Roo.isSafari){ // safari
24943             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24944             v =  (v < 10) ? 10 : v;
24945             v =  (v > 48) ? 48 : v;
24946             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24947             
24948         }
24949         
24950         
24951         v = Math.max(1, v+adjust);
24952         
24953         this.execCmd('FontSize', v  );
24954     },
24955
24956     onEditorEvent : function(e)
24957     {
24958         this.owner.fireEvent('editorevent', this, e);
24959       //  this.updateToolbar();
24960         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24961     },
24962
24963     insertTag : function(tg)
24964     {
24965         // could be a bit smarter... -> wrap the current selected tRoo..
24966         if (tg.toLowerCase() == 'span' ||
24967             tg.toLowerCase() == 'code' ||
24968             tg.toLowerCase() == 'sup' ||
24969             tg.toLowerCase() == 'sub' 
24970             ) {
24971             
24972             range = this.createRange(this.getSelection());
24973             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24974             wrappingNode.appendChild(range.extractContents());
24975             range.insertNode(wrappingNode);
24976
24977             return;
24978             
24979             
24980             
24981         }
24982         this.execCmd("formatblock",   tg);
24983         
24984     },
24985     
24986     insertText : function(txt)
24987     {
24988         
24989         
24990         var range = this.createRange();
24991         range.deleteContents();
24992                //alert(Sender.getAttribute('label'));
24993                
24994         range.insertNode(this.doc.createTextNode(txt));
24995     } ,
24996     
24997      
24998
24999     /**
25000      * Executes a Midas editor command on the editor document and performs necessary focus and
25001      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25002      * @param {String} cmd The Midas command
25003      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25004      */
25005     relayCmd : function(cmd, value){
25006         this.win.focus();
25007         this.execCmd(cmd, value);
25008         this.owner.fireEvent('editorevent', this);
25009         //this.updateToolbar();
25010         this.owner.deferFocus();
25011     },
25012
25013     /**
25014      * Executes a Midas editor command directly on the editor document.
25015      * For visual commands, you should use {@link #relayCmd} instead.
25016      * <b>This should only be called after the editor is initialized.</b>
25017      * @param {String} cmd The Midas command
25018      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25019      */
25020     execCmd : function(cmd, value){
25021         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25022         this.syncValue();
25023     },
25024  
25025  
25026    
25027     /**
25028      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25029      * to insert tRoo.
25030      * @param {String} text | dom node.. 
25031      */
25032     insertAtCursor : function(text)
25033     {
25034         
25035         if(!this.activated){
25036             return;
25037         }
25038         /*
25039         if(Roo.isIE){
25040             this.win.focus();
25041             var r = this.doc.selection.createRange();
25042             if(r){
25043                 r.collapse(true);
25044                 r.pasteHTML(text);
25045                 this.syncValue();
25046                 this.deferFocus();
25047             
25048             }
25049             return;
25050         }
25051         */
25052         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25053             this.win.focus();
25054             
25055             
25056             // from jquery ui (MIT licenced)
25057             var range, node;
25058             var win = this.win;
25059             
25060             if (win.getSelection && win.getSelection().getRangeAt) {
25061                 range = win.getSelection().getRangeAt(0);
25062                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25063                 range.insertNode(node);
25064             } else if (win.document.selection && win.document.selection.createRange) {
25065                 // no firefox support
25066                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25067                 win.document.selection.createRange().pasteHTML(txt);
25068             } else {
25069                 // no firefox support
25070                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25071                 this.execCmd('InsertHTML', txt);
25072             } 
25073             
25074             this.syncValue();
25075             
25076             this.deferFocus();
25077         }
25078     },
25079  // private
25080     mozKeyPress : function(e){
25081         if(e.ctrlKey){
25082             var c = e.getCharCode(), cmd;
25083           
25084             if(c > 0){
25085                 c = String.fromCharCode(c).toLowerCase();
25086                 switch(c){
25087                     case 'b':
25088                         cmd = 'bold';
25089                         break;
25090                     case 'i':
25091                         cmd = 'italic';
25092                         break;
25093                     
25094                     case 'u':
25095                         cmd = 'underline';
25096                         break;
25097                     
25098                     case 'v':
25099                         this.cleanUpPaste.defer(100, this);
25100                         return;
25101                         
25102                 }
25103                 if(cmd){
25104                     this.win.focus();
25105                     this.execCmd(cmd);
25106                     this.deferFocus();
25107                     e.preventDefault();
25108                 }
25109                 
25110             }
25111         }
25112     },
25113
25114     // private
25115     fixKeys : function(){ // load time branching for fastest keydown performance
25116         if(Roo.isIE){
25117             return function(e){
25118                 var k = e.getKey(), r;
25119                 if(k == e.TAB){
25120                     e.stopEvent();
25121                     r = this.doc.selection.createRange();
25122                     if(r){
25123                         r.collapse(true);
25124                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25125                         this.deferFocus();
25126                     }
25127                     return;
25128                 }
25129                 
25130                 if(k == e.ENTER){
25131                     r = this.doc.selection.createRange();
25132                     if(r){
25133                         var target = r.parentElement();
25134                         if(!target || target.tagName.toLowerCase() != 'li'){
25135                             e.stopEvent();
25136                             r.pasteHTML('<br />');
25137                             r.collapse(false);
25138                             r.select();
25139                         }
25140                     }
25141                 }
25142                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25143                     this.cleanUpPaste.defer(100, this);
25144                     return;
25145                 }
25146                 
25147                 
25148             };
25149         }else if(Roo.isOpera){
25150             return function(e){
25151                 var k = e.getKey();
25152                 if(k == e.TAB){
25153                     e.stopEvent();
25154                     this.win.focus();
25155                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25156                     this.deferFocus();
25157                 }
25158                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25159                     this.cleanUpPaste.defer(100, this);
25160                     return;
25161                 }
25162                 
25163             };
25164         }else if(Roo.isSafari){
25165             return function(e){
25166                 var k = e.getKey();
25167                 
25168                 if(k == e.TAB){
25169                     e.stopEvent();
25170                     this.execCmd('InsertText','\t');
25171                     this.deferFocus();
25172                     return;
25173                 }
25174                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25175                     this.cleanUpPaste.defer(100, this);
25176                     return;
25177                 }
25178                 
25179              };
25180         }
25181     }(),
25182     
25183     getAllAncestors: function()
25184     {
25185         var p = this.getSelectedNode();
25186         var a = [];
25187         if (!p) {
25188             a.push(p); // push blank onto stack..
25189             p = this.getParentElement();
25190         }
25191         
25192         
25193         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25194             a.push(p);
25195             p = p.parentNode;
25196         }
25197         a.push(this.doc.body);
25198         return a;
25199     },
25200     lastSel : false,
25201     lastSelNode : false,
25202     
25203     
25204     getSelection : function() 
25205     {
25206         this.assignDocWin();
25207         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25208     },
25209     
25210     getSelectedNode: function() 
25211     {
25212         // this may only work on Gecko!!!
25213         
25214         // should we cache this!!!!
25215         
25216         
25217         
25218          
25219         var range = this.createRange(this.getSelection()).cloneRange();
25220         
25221         if (Roo.isIE) {
25222             var parent = range.parentElement();
25223             while (true) {
25224                 var testRange = range.duplicate();
25225                 testRange.moveToElementText(parent);
25226                 if (testRange.inRange(range)) {
25227                     break;
25228                 }
25229                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25230                     break;
25231                 }
25232                 parent = parent.parentElement;
25233             }
25234             return parent;
25235         }
25236         
25237         // is ancestor a text element.
25238         var ac =  range.commonAncestorContainer;
25239         if (ac.nodeType == 3) {
25240             ac = ac.parentNode;
25241         }
25242         
25243         var ar = ac.childNodes;
25244          
25245         var nodes = [];
25246         var other_nodes = [];
25247         var has_other_nodes = false;
25248         for (var i=0;i<ar.length;i++) {
25249             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25250                 continue;
25251             }
25252             // fullly contained node.
25253             
25254             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25255                 nodes.push(ar[i]);
25256                 continue;
25257             }
25258             
25259             // probably selected..
25260             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25261                 other_nodes.push(ar[i]);
25262                 continue;
25263             }
25264             // outer..
25265             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25266                 continue;
25267             }
25268             
25269             
25270             has_other_nodes = true;
25271         }
25272         if (!nodes.length && other_nodes.length) {
25273             nodes= other_nodes;
25274         }
25275         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25276             return false;
25277         }
25278         
25279         return nodes[0];
25280     },
25281     createRange: function(sel)
25282     {
25283         // this has strange effects when using with 
25284         // top toolbar - not sure if it's a great idea.
25285         //this.editor.contentWindow.focus();
25286         if (typeof sel != "undefined") {
25287             try {
25288                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25289             } catch(e) {
25290                 return this.doc.createRange();
25291             }
25292         } else {
25293             return this.doc.createRange();
25294         }
25295     },
25296     getParentElement: function()
25297     {
25298         
25299         this.assignDocWin();
25300         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25301         
25302         var range = this.createRange(sel);
25303          
25304         try {
25305             var p = range.commonAncestorContainer;
25306             while (p.nodeType == 3) { // text node
25307                 p = p.parentNode;
25308             }
25309             return p;
25310         } catch (e) {
25311             return null;
25312         }
25313     
25314     },
25315     /***
25316      *
25317      * Range intersection.. the hard stuff...
25318      *  '-1' = before
25319      *  '0' = hits..
25320      *  '1' = after.
25321      *         [ -- selected range --- ]
25322      *   [fail]                        [fail]
25323      *
25324      *    basically..
25325      *      if end is before start or  hits it. fail.
25326      *      if start is after end or hits it fail.
25327      *
25328      *   if either hits (but other is outside. - then it's not 
25329      *   
25330      *    
25331      **/
25332     
25333     
25334     // @see http://www.thismuchiknow.co.uk/?p=64.
25335     rangeIntersectsNode : function(range, node)
25336     {
25337         var nodeRange = node.ownerDocument.createRange();
25338         try {
25339             nodeRange.selectNode(node);
25340         } catch (e) {
25341             nodeRange.selectNodeContents(node);
25342         }
25343     
25344         var rangeStartRange = range.cloneRange();
25345         rangeStartRange.collapse(true);
25346     
25347         var rangeEndRange = range.cloneRange();
25348         rangeEndRange.collapse(false);
25349     
25350         var nodeStartRange = nodeRange.cloneRange();
25351         nodeStartRange.collapse(true);
25352     
25353         var nodeEndRange = nodeRange.cloneRange();
25354         nodeEndRange.collapse(false);
25355     
25356         return rangeStartRange.compareBoundaryPoints(
25357                  Range.START_TO_START, nodeEndRange) == -1 &&
25358                rangeEndRange.compareBoundaryPoints(
25359                  Range.START_TO_START, nodeStartRange) == 1;
25360         
25361          
25362     },
25363     rangeCompareNode : function(range, node)
25364     {
25365         var nodeRange = node.ownerDocument.createRange();
25366         try {
25367             nodeRange.selectNode(node);
25368         } catch (e) {
25369             nodeRange.selectNodeContents(node);
25370         }
25371         
25372         
25373         range.collapse(true);
25374     
25375         nodeRange.collapse(true);
25376      
25377         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25378         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25379          
25380         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25381         
25382         var nodeIsBefore   =  ss == 1;
25383         var nodeIsAfter    = ee == -1;
25384         
25385         if (nodeIsBefore && nodeIsAfter) {
25386             return 0; // outer
25387         }
25388         if (!nodeIsBefore && nodeIsAfter) {
25389             return 1; //right trailed.
25390         }
25391         
25392         if (nodeIsBefore && !nodeIsAfter) {
25393             return 2;  // left trailed.
25394         }
25395         // fully contined.
25396         return 3;
25397     },
25398
25399     // private? - in a new class?
25400     cleanUpPaste :  function()
25401     {
25402         // cleans up the whole document..
25403         Roo.log('cleanuppaste');
25404         
25405         this.cleanUpChildren(this.doc.body);
25406         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25407         if (clean != this.doc.body.innerHTML) {
25408             this.doc.body.innerHTML = clean;
25409         }
25410         
25411     },
25412     
25413     cleanWordChars : function(input) {// change the chars to hex code
25414         var he = Roo.HtmlEditorCore;
25415         
25416         var output = input;
25417         Roo.each(he.swapCodes, function(sw) { 
25418             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25419             
25420             output = output.replace(swapper, sw[1]);
25421         });
25422         
25423         return output;
25424     },
25425     
25426     
25427     cleanUpChildren : function (n)
25428     {
25429         if (!n.childNodes.length) {
25430             return;
25431         }
25432         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25433            this.cleanUpChild(n.childNodes[i]);
25434         }
25435     },
25436     
25437     
25438         
25439     
25440     cleanUpChild : function (node)
25441     {
25442         var ed = this;
25443         //console.log(node);
25444         if (node.nodeName == "#text") {
25445             // clean up silly Windows -- stuff?
25446             return; 
25447         }
25448         if (node.nodeName == "#comment") {
25449             node.parentNode.removeChild(node);
25450             // clean up silly Windows -- stuff?
25451             return; 
25452         }
25453         var lcname = node.tagName.toLowerCase();
25454         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25455         // whitelist of tags..
25456         
25457         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25458             // remove node.
25459             node.parentNode.removeChild(node);
25460             return;
25461             
25462         }
25463         
25464         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25465         
25466         // spans with no attributes - just remove them..
25467         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25468             remove_keep_children = true;
25469         }
25470         
25471         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25472         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25473         
25474         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25475         //    remove_keep_children = true;
25476         //}
25477         
25478         if (remove_keep_children) {
25479             this.cleanUpChildren(node);
25480             // inserts everything just before this node...
25481             while (node.childNodes.length) {
25482                 var cn = node.childNodes[0];
25483                 node.removeChild(cn);
25484                 node.parentNode.insertBefore(cn, node);
25485             }
25486             node.parentNode.removeChild(node);
25487             return;
25488         }
25489         
25490         if (!node.attributes || !node.attributes.length) {
25491             
25492           
25493             
25494             
25495             this.cleanUpChildren(node);
25496             return;
25497         }
25498         
25499         function cleanAttr(n,v)
25500         {
25501             
25502             if (v.match(/^\./) || v.match(/^\//)) {
25503                 return;
25504             }
25505             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25506                 return;
25507             }
25508             if (v.match(/^#/)) {
25509                 return;
25510             }
25511             if (v.match(/^\{/)) { // allow template editing.
25512                 return;
25513             }
25514 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25515             node.removeAttribute(n);
25516             
25517         }
25518         
25519         var cwhite = this.cwhite;
25520         var cblack = this.cblack;
25521             
25522         function cleanStyle(n,v)
25523         {
25524             if (v.match(/expression/)) { //XSS?? should we even bother..
25525                 node.removeAttribute(n);
25526                 return;
25527             }
25528             
25529             var parts = v.split(/;/);
25530             var clean = [];
25531             
25532             Roo.each(parts, function(p) {
25533                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25534                 if (!p.length) {
25535                     return true;
25536                 }
25537                 var l = p.split(':').shift().replace(/\s+/g,'');
25538                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25539                 
25540                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25541 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25542                     //node.removeAttribute(n);
25543                     return true;
25544                 }
25545                 //Roo.log()
25546                 // only allow 'c whitelisted system attributes'
25547                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25548 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25549                     //node.removeAttribute(n);
25550                     return true;
25551                 }
25552                 
25553                 
25554                  
25555                 
25556                 clean.push(p);
25557                 return true;
25558             });
25559             if (clean.length) { 
25560                 node.setAttribute(n, clean.join(';'));
25561             } else {
25562                 node.removeAttribute(n);
25563             }
25564             
25565         }
25566         
25567         
25568         for (var i = node.attributes.length-1; i > -1 ; i--) {
25569             var a = node.attributes[i];
25570             //console.log(a);
25571             
25572             if (a.name.toLowerCase().substr(0,2)=='on')  {
25573                 node.removeAttribute(a.name);
25574                 continue;
25575             }
25576             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25577                 node.removeAttribute(a.name);
25578                 continue;
25579             }
25580             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25581                 cleanAttr(a.name,a.value); // fixme..
25582                 continue;
25583             }
25584             if (a.name == 'style') {
25585                 cleanStyle(a.name,a.value);
25586                 continue;
25587             }
25588             /// clean up MS crap..
25589             // tecnically this should be a list of valid class'es..
25590             
25591             
25592             if (a.name == 'class') {
25593                 if (a.value.match(/^Mso/)) {
25594                     node.removeAttribute('class');
25595                 }
25596                 
25597                 if (a.value.match(/^body$/)) {
25598                     node.removeAttribute('class');
25599                 }
25600                 continue;
25601             }
25602             
25603             // style cleanup!?
25604             // class cleanup?
25605             
25606         }
25607         
25608         
25609         this.cleanUpChildren(node);
25610         
25611         
25612     },
25613     
25614     /**
25615      * Clean up MS wordisms...
25616      */
25617     cleanWord : function(node)
25618     {
25619         if (!node) {
25620             this.cleanWord(this.doc.body);
25621             return;
25622         }
25623         
25624         if(
25625                 node.nodeName == 'SPAN' &&
25626                 !node.hasAttributes() &&
25627                 node.childNodes.length == 1 &&
25628                 node.firstChild.nodeName == "#text"  
25629         ) {
25630             var textNode = node.firstChild;
25631             node.removeChild(textNode);
25632             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25633                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25634             }
25635             node.parentNode.insertBefore(textNode, node);
25636             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25637                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25638             }
25639             node.parentNode.removeChild(node);
25640         }
25641         
25642         if (node.nodeName == "#text") {
25643             // clean up silly Windows -- stuff?
25644             return; 
25645         }
25646         if (node.nodeName == "#comment") {
25647             node.parentNode.removeChild(node);
25648             // clean up silly Windows -- stuff?
25649             return; 
25650         }
25651         
25652         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25653             node.parentNode.removeChild(node);
25654             return;
25655         }
25656         //Roo.log(node.tagName);
25657         // remove - but keep children..
25658         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25659             //Roo.log('-- removed');
25660             while (node.childNodes.length) {
25661                 var cn = node.childNodes[0];
25662                 node.removeChild(cn);
25663                 node.parentNode.insertBefore(cn, node);
25664                 // move node to parent - and clean it..
25665                 this.cleanWord(cn);
25666             }
25667             node.parentNode.removeChild(node);
25668             /// no need to iterate chidlren = it's got none..
25669             //this.iterateChildren(node, this.cleanWord);
25670             return;
25671         }
25672         // clean styles
25673         if (node.className.length) {
25674             
25675             var cn = node.className.split(/\W+/);
25676             var cna = [];
25677             Roo.each(cn, function(cls) {
25678                 if (cls.match(/Mso[a-zA-Z]+/)) {
25679                     return;
25680                 }
25681                 cna.push(cls);
25682             });
25683             node.className = cna.length ? cna.join(' ') : '';
25684             if (!cna.length) {
25685                 node.removeAttribute("class");
25686             }
25687         }
25688         
25689         if (node.hasAttribute("lang")) {
25690             node.removeAttribute("lang");
25691         }
25692         
25693         if (node.hasAttribute("style")) {
25694             
25695             var styles = node.getAttribute("style").split(";");
25696             var nstyle = [];
25697             Roo.each(styles, function(s) {
25698                 if (!s.match(/:/)) {
25699                     return;
25700                 }
25701                 var kv = s.split(":");
25702                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25703                     return;
25704                 }
25705                 // what ever is left... we allow.
25706                 nstyle.push(s);
25707             });
25708             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25709             if (!nstyle.length) {
25710                 node.removeAttribute('style');
25711             }
25712         }
25713         this.iterateChildren(node, this.cleanWord);
25714         
25715         
25716         
25717     },
25718     /**
25719      * iterateChildren of a Node, calling fn each time, using this as the scole..
25720      * @param {DomNode} node node to iterate children of.
25721      * @param {Function} fn method of this class to call on each item.
25722      */
25723     iterateChildren : function(node, fn)
25724     {
25725         if (!node.childNodes.length) {
25726                 return;
25727         }
25728         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25729            fn.call(this, node.childNodes[i])
25730         }
25731     },
25732     
25733     
25734     /**
25735      * cleanTableWidths.
25736      *
25737      * Quite often pasting from word etc.. results in tables with column and widths.
25738      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25739      *
25740      */
25741     cleanTableWidths : function(node)
25742     {
25743          
25744          
25745         if (!node) {
25746             this.cleanTableWidths(this.doc.body);
25747             return;
25748         }
25749         
25750         // ignore list...
25751         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25752             return; 
25753         }
25754         Roo.log(node.tagName);
25755         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25756             this.iterateChildren(node, this.cleanTableWidths);
25757             return;
25758         }
25759         if (node.hasAttribute('width')) {
25760             node.removeAttribute('width');
25761         }
25762         
25763          
25764         if (node.hasAttribute("style")) {
25765             // pretty basic...
25766             
25767             var styles = node.getAttribute("style").split(";");
25768             var nstyle = [];
25769             Roo.each(styles, function(s) {
25770                 if (!s.match(/:/)) {
25771                     return;
25772                 }
25773                 var kv = s.split(":");
25774                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25775                     return;
25776                 }
25777                 // what ever is left... we allow.
25778                 nstyle.push(s);
25779             });
25780             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25781             if (!nstyle.length) {
25782                 node.removeAttribute('style');
25783             }
25784         }
25785         
25786         this.iterateChildren(node, this.cleanTableWidths);
25787         
25788         
25789     },
25790     
25791     
25792     
25793     
25794     domToHTML : function(currentElement, depth, nopadtext) {
25795         
25796         depth = depth || 0;
25797         nopadtext = nopadtext || false;
25798     
25799         if (!currentElement) {
25800             return this.domToHTML(this.doc.body);
25801         }
25802         
25803         //Roo.log(currentElement);
25804         var j;
25805         var allText = false;
25806         var nodeName = currentElement.nodeName;
25807         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25808         
25809         if  (nodeName == '#text') {
25810             
25811             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25812         }
25813         
25814         
25815         var ret = '';
25816         if (nodeName != 'BODY') {
25817              
25818             var i = 0;
25819             // Prints the node tagName, such as <A>, <IMG>, etc
25820             if (tagName) {
25821                 var attr = [];
25822                 for(i = 0; i < currentElement.attributes.length;i++) {
25823                     // quoting?
25824                     var aname = currentElement.attributes.item(i).name;
25825                     if (!currentElement.attributes.item(i).value.length) {
25826                         continue;
25827                     }
25828                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25829                 }
25830                 
25831                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25832             } 
25833             else {
25834                 
25835                 // eack
25836             }
25837         } else {
25838             tagName = false;
25839         }
25840         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25841             return ret;
25842         }
25843         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25844             nopadtext = true;
25845         }
25846         
25847         
25848         // Traverse the tree
25849         i = 0;
25850         var currentElementChild = currentElement.childNodes.item(i);
25851         var allText = true;
25852         var innerHTML  = '';
25853         lastnode = '';
25854         while (currentElementChild) {
25855             // Formatting code (indent the tree so it looks nice on the screen)
25856             var nopad = nopadtext;
25857             if (lastnode == 'SPAN') {
25858                 nopad  = true;
25859             }
25860             // text
25861             if  (currentElementChild.nodeName == '#text') {
25862                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25863                 toadd = nopadtext ? toadd : toadd.trim();
25864                 if (!nopad && toadd.length > 80) {
25865                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25866                 }
25867                 innerHTML  += toadd;
25868                 
25869                 i++;
25870                 currentElementChild = currentElement.childNodes.item(i);
25871                 lastNode = '';
25872                 continue;
25873             }
25874             allText = false;
25875             
25876             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25877                 
25878             // Recursively traverse the tree structure of the child node
25879             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25880             lastnode = currentElementChild.nodeName;
25881             i++;
25882             currentElementChild=currentElement.childNodes.item(i);
25883         }
25884         
25885         ret += innerHTML;
25886         
25887         if (!allText) {
25888                 // The remaining code is mostly for formatting the tree
25889             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25890         }
25891         
25892         
25893         if (tagName) {
25894             ret+= "</"+tagName+">";
25895         }
25896         return ret;
25897         
25898     },
25899         
25900     applyBlacklists : function()
25901     {
25902         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25903         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25904         
25905         this.white = [];
25906         this.black = [];
25907         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25908             if (b.indexOf(tag) > -1) {
25909                 return;
25910             }
25911             this.white.push(tag);
25912             
25913         }, this);
25914         
25915         Roo.each(w, function(tag) {
25916             if (b.indexOf(tag) > -1) {
25917                 return;
25918             }
25919             if (this.white.indexOf(tag) > -1) {
25920                 return;
25921             }
25922             this.white.push(tag);
25923             
25924         }, this);
25925         
25926         
25927         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25928             if (w.indexOf(tag) > -1) {
25929                 return;
25930             }
25931             this.black.push(tag);
25932             
25933         }, this);
25934         
25935         Roo.each(b, function(tag) {
25936             if (w.indexOf(tag) > -1) {
25937                 return;
25938             }
25939             if (this.black.indexOf(tag) > -1) {
25940                 return;
25941             }
25942             this.black.push(tag);
25943             
25944         }, this);
25945         
25946         
25947         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25948         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25949         
25950         this.cwhite = [];
25951         this.cblack = [];
25952         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25953             if (b.indexOf(tag) > -1) {
25954                 return;
25955             }
25956             this.cwhite.push(tag);
25957             
25958         }, this);
25959         
25960         Roo.each(w, function(tag) {
25961             if (b.indexOf(tag) > -1) {
25962                 return;
25963             }
25964             if (this.cwhite.indexOf(tag) > -1) {
25965                 return;
25966             }
25967             this.cwhite.push(tag);
25968             
25969         }, this);
25970         
25971         
25972         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25973             if (w.indexOf(tag) > -1) {
25974                 return;
25975             }
25976             this.cblack.push(tag);
25977             
25978         }, this);
25979         
25980         Roo.each(b, function(tag) {
25981             if (w.indexOf(tag) > -1) {
25982                 return;
25983             }
25984             if (this.cblack.indexOf(tag) > -1) {
25985                 return;
25986             }
25987             this.cblack.push(tag);
25988             
25989         }, this);
25990     },
25991     
25992     setStylesheets : function(stylesheets)
25993     {
25994         if(typeof(stylesheets) == 'string'){
25995             Roo.get(this.iframe.contentDocument.head).createChild({
25996                 tag : 'link',
25997                 rel : 'stylesheet',
25998                 type : 'text/css',
25999                 href : stylesheets
26000             });
26001             
26002             return;
26003         }
26004         var _this = this;
26005      
26006         Roo.each(stylesheets, function(s) {
26007             if(!s.length){
26008                 return;
26009             }
26010             
26011             Roo.get(_this.iframe.contentDocument.head).createChild({
26012                 tag : 'link',
26013                 rel : 'stylesheet',
26014                 type : 'text/css',
26015                 href : s
26016             });
26017         });
26018
26019         
26020     },
26021     
26022     removeStylesheets : function()
26023     {
26024         var _this = this;
26025         
26026         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26027             s.remove();
26028         });
26029     },
26030     
26031     setStyle : function(style)
26032     {
26033         Roo.get(this.iframe.contentDocument.head).createChild({
26034             tag : 'style',
26035             type : 'text/css',
26036             html : style
26037         });
26038
26039         return;
26040     }
26041     
26042     // hide stuff that is not compatible
26043     /**
26044      * @event blur
26045      * @hide
26046      */
26047     /**
26048      * @event change
26049      * @hide
26050      */
26051     /**
26052      * @event focus
26053      * @hide
26054      */
26055     /**
26056      * @event specialkey
26057      * @hide
26058      */
26059     /**
26060      * @cfg {String} fieldClass @hide
26061      */
26062     /**
26063      * @cfg {String} focusClass @hide
26064      */
26065     /**
26066      * @cfg {String} autoCreate @hide
26067      */
26068     /**
26069      * @cfg {String} inputType @hide
26070      */
26071     /**
26072      * @cfg {String} invalidClass @hide
26073      */
26074     /**
26075      * @cfg {String} invalidText @hide
26076      */
26077     /**
26078      * @cfg {String} msgFx @hide
26079      */
26080     /**
26081      * @cfg {String} validateOnBlur @hide
26082      */
26083 });
26084
26085 Roo.HtmlEditorCore.white = [
26086         'area', 'br', 'img', 'input', 'hr', 'wbr',
26087         
26088        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26089        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26090        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26091        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26092        'table',   'ul',         'xmp', 
26093        
26094        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26095       'thead',   'tr', 
26096      
26097       'dir', 'menu', 'ol', 'ul', 'dl',
26098        
26099       'embed',  'object'
26100 ];
26101
26102
26103 Roo.HtmlEditorCore.black = [
26104     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26105         'applet', // 
26106         'base',   'basefont', 'bgsound', 'blink',  'body', 
26107         'frame',  'frameset', 'head',    'html',   'ilayer', 
26108         'iframe', 'layer',  'link',     'meta',    'object',   
26109         'script', 'style' ,'title',  'xml' // clean later..
26110 ];
26111 Roo.HtmlEditorCore.clean = [
26112     'script', 'style', 'title', 'xml'
26113 ];
26114 Roo.HtmlEditorCore.remove = [
26115     'font'
26116 ];
26117 // attributes..
26118
26119 Roo.HtmlEditorCore.ablack = [
26120     'on'
26121 ];
26122     
26123 Roo.HtmlEditorCore.aclean = [ 
26124     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26125 ];
26126
26127 // protocols..
26128 Roo.HtmlEditorCore.pwhite= [
26129         'http',  'https',  'mailto'
26130 ];
26131
26132 // white listed style attributes.
26133 Roo.HtmlEditorCore.cwhite= [
26134       //  'text-align', /// default is to allow most things..
26135       
26136          
26137 //        'font-size'//??
26138 ];
26139
26140 // black listed style attributes.
26141 Roo.HtmlEditorCore.cblack= [
26142       //  'font-size' -- this can be set by the project 
26143 ];
26144
26145
26146 Roo.HtmlEditorCore.swapCodes   =[ 
26147     [    8211, "&#8211;" ], 
26148     [    8212, "&#8212;" ], 
26149     [    8216,  "'" ],  
26150     [    8217, "'" ],  
26151     [    8220, '"' ],  
26152     [    8221, '"' ],  
26153     [    8226, "*" ],  
26154     [    8230, "..." ]
26155 ]; 
26156
26157     /*
26158  * - LGPL
26159  *
26160  * HtmlEditor
26161  * 
26162  */
26163
26164 /**
26165  * @class Roo.bootstrap.HtmlEditor
26166  * @extends Roo.bootstrap.TextArea
26167  * Bootstrap HtmlEditor class
26168
26169  * @constructor
26170  * Create a new HtmlEditor
26171  * @param {Object} config The config object
26172  */
26173
26174 Roo.bootstrap.HtmlEditor = function(config){
26175     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26176     if (!this.toolbars) {
26177         this.toolbars = [];
26178     }
26179     
26180     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26181     this.addEvents({
26182             /**
26183              * @event initialize
26184              * Fires when the editor is fully initialized (including the iframe)
26185              * @param {HtmlEditor} this
26186              */
26187             initialize: true,
26188             /**
26189              * @event activate
26190              * Fires when the editor is first receives the focus. Any insertion must wait
26191              * until after this event.
26192              * @param {HtmlEditor} this
26193              */
26194             activate: true,
26195              /**
26196              * @event beforesync
26197              * Fires before the textarea is updated with content from the editor iframe. Return false
26198              * to cancel the sync.
26199              * @param {HtmlEditor} this
26200              * @param {String} html
26201              */
26202             beforesync: true,
26203              /**
26204              * @event beforepush
26205              * Fires before the iframe editor is updated with content from the textarea. Return false
26206              * to cancel the push.
26207              * @param {HtmlEditor} this
26208              * @param {String} html
26209              */
26210             beforepush: true,
26211              /**
26212              * @event sync
26213              * Fires when the textarea is updated with content from the editor iframe.
26214              * @param {HtmlEditor} this
26215              * @param {String} html
26216              */
26217             sync: true,
26218              /**
26219              * @event push
26220              * Fires when the iframe editor is updated with content from the textarea.
26221              * @param {HtmlEditor} this
26222              * @param {String} html
26223              */
26224             push: true,
26225              /**
26226              * @event editmodechange
26227              * Fires when the editor switches edit modes
26228              * @param {HtmlEditor} this
26229              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26230              */
26231             editmodechange: true,
26232             /**
26233              * @event editorevent
26234              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26235              * @param {HtmlEditor} this
26236              */
26237             editorevent: true,
26238             /**
26239              * @event firstfocus
26240              * Fires when on first focus - needed by toolbars..
26241              * @param {HtmlEditor} this
26242              */
26243             firstfocus: true,
26244             /**
26245              * @event autosave
26246              * Auto save the htmlEditor value as a file into Events
26247              * @param {HtmlEditor} this
26248              */
26249             autosave: true,
26250             /**
26251              * @event savedpreview
26252              * preview the saved version of htmlEditor
26253              * @param {HtmlEditor} this
26254              */
26255             savedpreview: true
26256         });
26257 };
26258
26259
26260 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26261     
26262     
26263       /**
26264      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26265      */
26266     toolbars : false,
26267     
26268      /**
26269     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26270     */
26271     btns : [],
26272    
26273      /**
26274      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26275      *                        Roo.resizable.
26276      */
26277     resizable : false,
26278      /**
26279      * @cfg {Number} height (in pixels)
26280      */   
26281     height: 300,
26282    /**
26283      * @cfg {Number} width (in pixels)
26284      */   
26285     width: false,
26286     
26287     /**
26288      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26289      * 
26290      */
26291     stylesheets: false,
26292     
26293     // id of frame..
26294     frameId: false,
26295     
26296     // private properties
26297     validationEvent : false,
26298     deferHeight: true,
26299     initialized : false,
26300     activated : false,
26301     
26302     onFocus : Roo.emptyFn,
26303     iframePad:3,
26304     hideMode:'offsets',
26305     
26306     tbContainer : false,
26307     
26308     bodyCls : '',
26309     
26310     toolbarContainer :function() {
26311         return this.wrap.select('.x-html-editor-tb',true).first();
26312     },
26313
26314     /**
26315      * Protected method that will not generally be called directly. It
26316      * is called when the editor creates its toolbar. Override this method if you need to
26317      * add custom toolbar buttons.
26318      * @param {HtmlEditor} editor
26319      */
26320     createToolbar : function(){
26321         Roo.log('renewing');
26322         Roo.log("create toolbars");
26323         
26324         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26325         this.toolbars[0].render(this.toolbarContainer());
26326         
26327         return;
26328         
26329 //        if (!editor.toolbars || !editor.toolbars.length) {
26330 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26331 //        }
26332 //        
26333 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26334 //            editor.toolbars[i] = Roo.factory(
26335 //                    typeof(editor.toolbars[i]) == 'string' ?
26336 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26337 //                Roo.bootstrap.HtmlEditor);
26338 //            editor.toolbars[i].init(editor);
26339 //        }
26340     },
26341
26342      
26343     // private
26344     onRender : function(ct, position)
26345     {
26346        // Roo.log("Call onRender: " + this.xtype);
26347         var _t = this;
26348         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26349       
26350         this.wrap = this.inputEl().wrap({
26351             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26352         });
26353         
26354         this.editorcore.onRender(ct, position);
26355          
26356         if (this.resizable) {
26357             this.resizeEl = new Roo.Resizable(this.wrap, {
26358                 pinned : true,
26359                 wrap: true,
26360                 dynamic : true,
26361                 minHeight : this.height,
26362                 height: this.height,
26363                 handles : this.resizable,
26364                 width: this.width,
26365                 listeners : {
26366                     resize : function(r, w, h) {
26367                         _t.onResize(w,h); // -something
26368                     }
26369                 }
26370             });
26371             
26372         }
26373         this.createToolbar(this);
26374        
26375         
26376         if(!this.width && this.resizable){
26377             this.setSize(this.wrap.getSize());
26378         }
26379         if (this.resizeEl) {
26380             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26381             // should trigger onReize..
26382         }
26383         
26384     },
26385
26386     // private
26387     onResize : function(w, h)
26388     {
26389         Roo.log('resize: ' +w + ',' + h );
26390         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26391         var ew = false;
26392         var eh = false;
26393         
26394         if(this.inputEl() ){
26395             if(typeof w == 'number'){
26396                 var aw = w - this.wrap.getFrameWidth('lr');
26397                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26398                 ew = aw;
26399             }
26400             if(typeof h == 'number'){
26401                  var tbh = -11;  // fixme it needs to tool bar size!
26402                 for (var i =0; i < this.toolbars.length;i++) {
26403                     // fixme - ask toolbars for heights?
26404                     tbh += this.toolbars[i].el.getHeight();
26405                     //if (this.toolbars[i].footer) {
26406                     //    tbh += this.toolbars[i].footer.el.getHeight();
26407                     //}
26408                 }
26409               
26410                 
26411                 
26412                 
26413                 
26414                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26415                 ah -= 5; // knock a few pixes off for look..
26416                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26417                 var eh = ah;
26418             }
26419         }
26420         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26421         this.editorcore.onResize(ew,eh);
26422         
26423     },
26424
26425     /**
26426      * Toggles the editor between standard and source edit mode.
26427      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26428      */
26429     toggleSourceEdit : function(sourceEditMode)
26430     {
26431         this.editorcore.toggleSourceEdit(sourceEditMode);
26432         
26433         if(this.editorcore.sourceEditMode){
26434             Roo.log('editor - showing textarea');
26435             
26436 //            Roo.log('in');
26437 //            Roo.log(this.syncValue());
26438             this.syncValue();
26439             this.inputEl().removeClass(['hide', 'x-hidden']);
26440             this.inputEl().dom.removeAttribute('tabIndex');
26441             this.inputEl().focus();
26442         }else{
26443             Roo.log('editor - hiding textarea');
26444 //            Roo.log('out')
26445 //            Roo.log(this.pushValue()); 
26446             this.pushValue();
26447             
26448             this.inputEl().addClass(['hide', 'x-hidden']);
26449             this.inputEl().dom.setAttribute('tabIndex', -1);
26450             //this.deferFocus();
26451         }
26452          
26453         if(this.resizable){
26454             this.setSize(this.wrap.getSize());
26455         }
26456         
26457         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26458     },
26459  
26460     // private (for BoxComponent)
26461     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26462
26463     // private (for BoxComponent)
26464     getResizeEl : function(){
26465         return this.wrap;
26466     },
26467
26468     // private (for BoxComponent)
26469     getPositionEl : function(){
26470         return this.wrap;
26471     },
26472
26473     // private
26474     initEvents : function(){
26475         this.originalValue = this.getValue();
26476     },
26477
26478 //    /**
26479 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26480 //     * @method
26481 //     */
26482 //    markInvalid : Roo.emptyFn,
26483 //    /**
26484 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26485 //     * @method
26486 //     */
26487 //    clearInvalid : Roo.emptyFn,
26488
26489     setValue : function(v){
26490         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26491         this.editorcore.pushValue();
26492     },
26493
26494      
26495     // private
26496     deferFocus : function(){
26497         this.focus.defer(10, this);
26498     },
26499
26500     // doc'ed in Field
26501     focus : function(){
26502         this.editorcore.focus();
26503         
26504     },
26505       
26506
26507     // private
26508     onDestroy : function(){
26509         
26510         
26511         
26512         if(this.rendered){
26513             
26514             for (var i =0; i < this.toolbars.length;i++) {
26515                 // fixme - ask toolbars for heights?
26516                 this.toolbars[i].onDestroy();
26517             }
26518             
26519             this.wrap.dom.innerHTML = '';
26520             this.wrap.remove();
26521         }
26522     },
26523
26524     // private
26525     onFirstFocus : function(){
26526         //Roo.log("onFirstFocus");
26527         this.editorcore.onFirstFocus();
26528          for (var i =0; i < this.toolbars.length;i++) {
26529             this.toolbars[i].onFirstFocus();
26530         }
26531         
26532     },
26533     
26534     // private
26535     syncValue : function()
26536     {   
26537         this.editorcore.syncValue();
26538     },
26539     
26540     pushValue : function()
26541     {   
26542         this.editorcore.pushValue();
26543     }
26544      
26545     
26546     // hide stuff that is not compatible
26547     /**
26548      * @event blur
26549      * @hide
26550      */
26551     /**
26552      * @event change
26553      * @hide
26554      */
26555     /**
26556      * @event focus
26557      * @hide
26558      */
26559     /**
26560      * @event specialkey
26561      * @hide
26562      */
26563     /**
26564      * @cfg {String} fieldClass @hide
26565      */
26566     /**
26567      * @cfg {String} focusClass @hide
26568      */
26569     /**
26570      * @cfg {String} autoCreate @hide
26571      */
26572     /**
26573      * @cfg {String} inputType @hide
26574      */
26575      
26576     /**
26577      * @cfg {String} invalidText @hide
26578      */
26579     /**
26580      * @cfg {String} msgFx @hide
26581      */
26582     /**
26583      * @cfg {String} validateOnBlur @hide
26584      */
26585 });
26586  
26587     
26588    
26589    
26590    
26591       
26592 Roo.namespace('Roo.bootstrap.htmleditor');
26593 /**
26594  * @class Roo.bootstrap.HtmlEditorToolbar1
26595  * Basic Toolbar
26596  * 
26597  * @example
26598  * Usage:
26599  *
26600  new Roo.bootstrap.HtmlEditor({
26601     ....
26602     toolbars : [
26603         new Roo.bootstrap.HtmlEditorToolbar1({
26604             disable : { fonts: 1 , format: 1, ..., ... , ...],
26605             btns : [ .... ]
26606         })
26607     }
26608      
26609  * 
26610  * @cfg {Object} disable List of elements to disable..
26611  * @cfg {Array} btns List of additional buttons.
26612  * 
26613  * 
26614  * NEEDS Extra CSS? 
26615  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26616  */
26617  
26618 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26619 {
26620     
26621     Roo.apply(this, config);
26622     
26623     // default disabled, based on 'good practice'..
26624     this.disable = this.disable || {};
26625     Roo.applyIf(this.disable, {
26626         fontSize : true,
26627         colors : true,
26628         specialElements : true
26629     });
26630     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26631     
26632     this.editor = config.editor;
26633     this.editorcore = config.editor.editorcore;
26634     
26635     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26636     
26637     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26638     // dont call parent... till later.
26639 }
26640 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26641      
26642     bar : true,
26643     
26644     editor : false,
26645     editorcore : false,
26646     
26647     
26648     formats : [
26649         "p" ,  
26650         "h1","h2","h3","h4","h5","h6", 
26651         "pre", "code", 
26652         "abbr", "acronym", "address", "cite", "samp", "var",
26653         'div','span'
26654     ],
26655     
26656     onRender : function(ct, position)
26657     {
26658        // Roo.log("Call onRender: " + this.xtype);
26659         
26660        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26661        Roo.log(this.el);
26662        this.el.dom.style.marginBottom = '0';
26663        var _this = this;
26664        var editorcore = this.editorcore;
26665        var editor= this.editor;
26666        
26667        var children = [];
26668        var btn = function(id,cmd , toggle, handler, html){
26669        
26670             var  event = toggle ? 'toggle' : 'click';
26671        
26672             var a = {
26673                 size : 'sm',
26674                 xtype: 'Button',
26675                 xns: Roo.bootstrap,
26676                 //glyphicon : id,
26677                 fa: id,
26678                 cmd : id || cmd,
26679                 enableToggle:toggle !== false,
26680                 html : html || '',
26681                 pressed : toggle ? false : null,
26682                 listeners : {}
26683             };
26684             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26685                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26686             };
26687             children.push(a);
26688             return a;
26689        }
26690        
26691     //    var cb_box = function...
26692         
26693         var style = {
26694                 xtype: 'Button',
26695                 size : 'sm',
26696                 xns: Roo.bootstrap,
26697                 fa : 'font',
26698                 //html : 'submit'
26699                 menu : {
26700                     xtype: 'Menu',
26701                     xns: Roo.bootstrap,
26702                     items:  []
26703                 }
26704         };
26705         Roo.each(this.formats, function(f) {
26706             style.menu.items.push({
26707                 xtype :'MenuItem',
26708                 xns: Roo.bootstrap,
26709                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26710                 tagname : f,
26711                 listeners : {
26712                     click : function()
26713                     {
26714                         editorcore.insertTag(this.tagname);
26715                         editor.focus();
26716                     }
26717                 }
26718                 
26719             });
26720         });
26721         children.push(style);   
26722         
26723         btn('bold',false,true);
26724         btn('italic',false,true);
26725         btn('align-left', 'justifyleft',true);
26726         btn('align-center', 'justifycenter',true);
26727         btn('align-right' , 'justifyright',true);
26728         btn('link', false, false, function(btn) {
26729             //Roo.log("create link?");
26730             var url = prompt(this.createLinkText, this.defaultLinkValue);
26731             if(url && url != 'http:/'+'/'){
26732                 this.editorcore.relayCmd('createlink', url);
26733             }
26734         }),
26735         btn('list','insertunorderedlist',true);
26736         btn('pencil', false,true, function(btn){
26737                 Roo.log(this);
26738                 this.toggleSourceEdit(btn.pressed);
26739         });
26740         
26741         if (this.editor.btns.length > 0) {
26742             for (var i = 0; i<this.editor.btns.length; i++) {
26743                 children.push(this.editor.btns[i]);
26744             }
26745         }
26746         
26747         /*
26748         var cog = {
26749                 xtype: 'Button',
26750                 size : 'sm',
26751                 xns: Roo.bootstrap,
26752                 glyphicon : 'cog',
26753                 //html : 'submit'
26754                 menu : {
26755                     xtype: 'Menu',
26756                     xns: Roo.bootstrap,
26757                     items:  []
26758                 }
26759         };
26760         
26761         cog.menu.items.push({
26762             xtype :'MenuItem',
26763             xns: Roo.bootstrap,
26764             html : Clean styles,
26765             tagname : f,
26766             listeners : {
26767                 click : function()
26768                 {
26769                     editorcore.insertTag(this.tagname);
26770                     editor.focus();
26771                 }
26772             }
26773             
26774         });
26775        */
26776         
26777          
26778        this.xtype = 'NavSimplebar';
26779         
26780         for(var i=0;i< children.length;i++) {
26781             
26782             this.buttons.add(this.addxtypeChild(children[i]));
26783             
26784         }
26785         
26786         editor.on('editorevent', this.updateToolbar, this);
26787     },
26788     onBtnClick : function(id)
26789     {
26790        this.editorcore.relayCmd(id);
26791        this.editorcore.focus();
26792     },
26793     
26794     /**
26795      * Protected method that will not generally be called directly. It triggers
26796      * a toolbar update by reading the markup state of the current selection in the editor.
26797      */
26798     updateToolbar: function(){
26799
26800         if(!this.editorcore.activated){
26801             this.editor.onFirstFocus(); // is this neeed?
26802             return;
26803         }
26804
26805         var btns = this.buttons; 
26806         var doc = this.editorcore.doc;
26807         btns.get('bold').setActive(doc.queryCommandState('bold'));
26808         btns.get('italic').setActive(doc.queryCommandState('italic'));
26809         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26810         
26811         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26812         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26813         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26814         
26815         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26816         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26817          /*
26818         
26819         var ans = this.editorcore.getAllAncestors();
26820         if (this.formatCombo) {
26821             
26822             
26823             var store = this.formatCombo.store;
26824             this.formatCombo.setValue("");
26825             for (var i =0; i < ans.length;i++) {
26826                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26827                     // select it..
26828                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26829                     break;
26830                 }
26831             }
26832         }
26833         
26834         
26835         
26836         // hides menus... - so this cant be on a menu...
26837         Roo.bootstrap.MenuMgr.hideAll();
26838         */
26839         Roo.bootstrap.MenuMgr.hideAll();
26840         //this.editorsyncValue();
26841     },
26842     onFirstFocus: function() {
26843         this.buttons.each(function(item){
26844            item.enable();
26845         });
26846     },
26847     toggleSourceEdit : function(sourceEditMode){
26848         
26849           
26850         if(sourceEditMode){
26851             Roo.log("disabling buttons");
26852            this.buttons.each( function(item){
26853                 if(item.cmd != 'pencil'){
26854                     item.disable();
26855                 }
26856             });
26857           
26858         }else{
26859             Roo.log("enabling buttons");
26860             if(this.editorcore.initialized){
26861                 this.buttons.each( function(item){
26862                     item.enable();
26863                 });
26864             }
26865             
26866         }
26867         Roo.log("calling toggole on editor");
26868         // tell the editor that it's been pressed..
26869         this.editor.toggleSourceEdit(sourceEditMode);
26870        
26871     }
26872 });
26873
26874
26875
26876
26877  
26878 /*
26879  * - LGPL
26880  */
26881
26882 /**
26883  * @class Roo.bootstrap.Markdown
26884  * @extends Roo.bootstrap.TextArea
26885  * Bootstrap Showdown editable area
26886  * @cfg {string} content
26887  * 
26888  * @constructor
26889  * Create a new Showdown
26890  */
26891
26892 Roo.bootstrap.Markdown = function(config){
26893     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26894    
26895 };
26896
26897 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26898     
26899     editing :false,
26900     
26901     initEvents : function()
26902     {
26903         
26904         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26905         this.markdownEl = this.el.createChild({
26906             cls : 'roo-markdown-area'
26907         });
26908         this.inputEl().addClass('d-none');
26909         if (this.getValue() == '') {
26910             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26911             
26912         } else {
26913             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26914         }
26915         this.markdownEl.on('click', this.toggleTextEdit, this);
26916         this.on('blur', this.toggleTextEdit, this);
26917         this.on('specialkey', this.resizeTextArea, this);
26918     },
26919     
26920     toggleTextEdit : function()
26921     {
26922         var sh = this.markdownEl.getHeight();
26923         this.inputEl().addClass('d-none');
26924         this.markdownEl.addClass('d-none');
26925         if (!this.editing) {
26926             // show editor?
26927             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26928             this.inputEl().removeClass('d-none');
26929             this.inputEl().focus();
26930             this.editing = true;
26931             return;
26932         }
26933         // show showdown...
26934         this.updateMarkdown();
26935         this.markdownEl.removeClass('d-none');
26936         this.editing = false;
26937         return;
26938     },
26939     updateMarkdown : function()
26940     {
26941         if (this.getValue() == '') {
26942             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26943             return;
26944         }
26945  
26946         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26947     },
26948     
26949     resizeTextArea: function () {
26950         
26951         var sh = 100;
26952         Roo.log([sh, this.getValue().split("\n").length * 30]);
26953         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26954     },
26955     setValue : function(val)
26956     {
26957         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26958         if (!this.editing) {
26959             this.updateMarkdown();
26960         }
26961         
26962     },
26963     focus : function()
26964     {
26965         if (!this.editing) {
26966             this.toggleTextEdit();
26967         }
26968         
26969     }
26970
26971
26972 });
26973 /**
26974  * @class Roo.bootstrap.Table.AbstractSelectionModel
26975  * @extends Roo.util.Observable
26976  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26977  * implemented by descendant classes.  This class should not be directly instantiated.
26978  * @constructor
26979  */
26980 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26981     this.locked = false;
26982     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26983 };
26984
26985
26986 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26987     /** @ignore Called by the grid automatically. Do not call directly. */
26988     init : function(grid){
26989         this.grid = grid;
26990         this.initEvents();
26991     },
26992
26993     /**
26994      * Locks the selections.
26995      */
26996     lock : function(){
26997         this.locked = true;
26998     },
26999
27000     /**
27001      * Unlocks the selections.
27002      */
27003     unlock : function(){
27004         this.locked = false;
27005     },
27006
27007     /**
27008      * Returns true if the selections are locked.
27009      * @return {Boolean}
27010      */
27011     isLocked : function(){
27012         return this.locked;
27013     },
27014     
27015     
27016     initEvents : function ()
27017     {
27018         
27019     }
27020 });
27021 /**
27022  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27023  * @class Roo.bootstrap.Table.RowSelectionModel
27024  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27025  * It supports multiple selections and keyboard selection/navigation. 
27026  * @constructor
27027  * @param {Object} config
27028  */
27029
27030 Roo.bootstrap.Table.RowSelectionModel = function(config){
27031     Roo.apply(this, config);
27032     this.selections = new Roo.util.MixedCollection(false, function(o){
27033         return o.id;
27034     });
27035
27036     this.last = false;
27037     this.lastActive = false;
27038
27039     this.addEvents({
27040         /**
27041              * @event selectionchange
27042              * Fires when the selection changes
27043              * @param {SelectionModel} this
27044              */
27045             "selectionchange" : true,
27046         /**
27047              * @event afterselectionchange
27048              * Fires after the selection changes (eg. by key press or clicking)
27049              * @param {SelectionModel} this
27050              */
27051             "afterselectionchange" : true,
27052         /**
27053              * @event beforerowselect
27054              * Fires when a row is selected being selected, return false to cancel.
27055              * @param {SelectionModel} this
27056              * @param {Number} rowIndex The selected index
27057              * @param {Boolean} keepExisting False if other selections will be cleared
27058              */
27059             "beforerowselect" : true,
27060         /**
27061              * @event rowselect
27062              * Fires when a row is selected.
27063              * @param {SelectionModel} this
27064              * @param {Number} rowIndex The selected index
27065              * @param {Roo.data.Record} r The record
27066              */
27067             "rowselect" : true,
27068         /**
27069              * @event rowdeselect
27070              * Fires when a row is deselected.
27071              * @param {SelectionModel} this
27072              * @param {Number} rowIndex The selected index
27073              */
27074         "rowdeselect" : true
27075     });
27076     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27077     this.locked = false;
27078  };
27079
27080 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27081     /**
27082      * @cfg {Boolean} singleSelect
27083      * True to allow selection of only one row at a time (defaults to false)
27084      */
27085     singleSelect : false,
27086
27087     // private
27088     initEvents : function()
27089     {
27090
27091         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27092         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27093         //}else{ // allow click to work like normal
27094          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27095         //}
27096         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27097         this.grid.on("rowclick", this.handleMouseDown, this);
27098         
27099         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27100             "up" : function(e){
27101                 if(!e.shiftKey){
27102                     this.selectPrevious(e.shiftKey);
27103                 }else if(this.last !== false && this.lastActive !== false){
27104                     var last = this.last;
27105                     this.selectRange(this.last,  this.lastActive-1);
27106                     this.grid.getView().focusRow(this.lastActive);
27107                     if(last !== false){
27108                         this.last = last;
27109                     }
27110                 }else{
27111                     this.selectFirstRow();
27112                 }
27113                 this.fireEvent("afterselectionchange", this);
27114             },
27115             "down" : function(e){
27116                 if(!e.shiftKey){
27117                     this.selectNext(e.shiftKey);
27118                 }else if(this.last !== false && this.lastActive !== false){
27119                     var last = this.last;
27120                     this.selectRange(this.last,  this.lastActive+1);
27121                     this.grid.getView().focusRow(this.lastActive);
27122                     if(last !== false){
27123                         this.last = last;
27124                     }
27125                 }else{
27126                     this.selectFirstRow();
27127                 }
27128                 this.fireEvent("afterselectionchange", this);
27129             },
27130             scope: this
27131         });
27132         this.grid.store.on('load', function(){
27133             this.selections.clear();
27134         },this);
27135         /*
27136         var view = this.grid.view;
27137         view.on("refresh", this.onRefresh, this);
27138         view.on("rowupdated", this.onRowUpdated, this);
27139         view.on("rowremoved", this.onRemove, this);
27140         */
27141     },
27142
27143     // private
27144     onRefresh : function()
27145     {
27146         var ds = this.grid.store, i, v = this.grid.view;
27147         var s = this.selections;
27148         s.each(function(r){
27149             if((i = ds.indexOfId(r.id)) != -1){
27150                 v.onRowSelect(i);
27151             }else{
27152                 s.remove(r);
27153             }
27154         });
27155     },
27156
27157     // private
27158     onRemove : function(v, index, r){
27159         this.selections.remove(r);
27160     },
27161
27162     // private
27163     onRowUpdated : function(v, index, r){
27164         if(this.isSelected(r)){
27165             v.onRowSelect(index);
27166         }
27167     },
27168
27169     /**
27170      * Select records.
27171      * @param {Array} records The records to select
27172      * @param {Boolean} keepExisting (optional) True to keep existing selections
27173      */
27174     selectRecords : function(records, keepExisting)
27175     {
27176         if(!keepExisting){
27177             this.clearSelections();
27178         }
27179             var ds = this.grid.store;
27180         for(var i = 0, len = records.length; i < len; i++){
27181             this.selectRow(ds.indexOf(records[i]), true);
27182         }
27183     },
27184
27185     /**
27186      * Gets the number of selected rows.
27187      * @return {Number}
27188      */
27189     getCount : function(){
27190         return this.selections.length;
27191     },
27192
27193     /**
27194      * Selects the first row in the grid.
27195      */
27196     selectFirstRow : function(){
27197         this.selectRow(0);
27198     },
27199
27200     /**
27201      * Select the last row.
27202      * @param {Boolean} keepExisting (optional) True to keep existing selections
27203      */
27204     selectLastRow : function(keepExisting){
27205         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27206         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27207     },
27208
27209     /**
27210      * Selects the row immediately following the last selected row.
27211      * @param {Boolean} keepExisting (optional) True to keep existing selections
27212      */
27213     selectNext : function(keepExisting)
27214     {
27215             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27216             this.selectRow(this.last+1, keepExisting);
27217             this.grid.getView().focusRow(this.last);
27218         }
27219     },
27220
27221     /**
27222      * Selects the row that precedes the last selected row.
27223      * @param {Boolean} keepExisting (optional) True to keep existing selections
27224      */
27225     selectPrevious : function(keepExisting){
27226         if(this.last){
27227             this.selectRow(this.last-1, keepExisting);
27228             this.grid.getView().focusRow(this.last);
27229         }
27230     },
27231
27232     /**
27233      * Returns the selected records
27234      * @return {Array} Array of selected records
27235      */
27236     getSelections : function(){
27237         return [].concat(this.selections.items);
27238     },
27239
27240     /**
27241      * Returns the first selected record.
27242      * @return {Record}
27243      */
27244     getSelected : function(){
27245         return this.selections.itemAt(0);
27246     },
27247
27248
27249     /**
27250      * Clears all selections.
27251      */
27252     clearSelections : function(fast)
27253     {
27254         if(this.locked) {
27255             return;
27256         }
27257         if(fast !== true){
27258                 var ds = this.grid.store;
27259             var s = this.selections;
27260             s.each(function(r){
27261                 this.deselectRow(ds.indexOfId(r.id));
27262             }, this);
27263             s.clear();
27264         }else{
27265             this.selections.clear();
27266         }
27267         this.last = false;
27268     },
27269
27270
27271     /**
27272      * Selects all rows.
27273      */
27274     selectAll : function(){
27275         if(this.locked) {
27276             return;
27277         }
27278         this.selections.clear();
27279         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27280             this.selectRow(i, true);
27281         }
27282     },
27283
27284     /**
27285      * Returns True if there is a selection.
27286      * @return {Boolean}
27287      */
27288     hasSelection : function(){
27289         return this.selections.length > 0;
27290     },
27291
27292     /**
27293      * Returns True if the specified row is selected.
27294      * @param {Number/Record} record The record or index of the record to check
27295      * @return {Boolean}
27296      */
27297     isSelected : function(index){
27298             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27299         return (r && this.selections.key(r.id) ? true : false);
27300     },
27301
27302     /**
27303      * Returns True if the specified record id is selected.
27304      * @param {String} id The id of record to check
27305      * @return {Boolean}
27306      */
27307     isIdSelected : function(id){
27308         return (this.selections.key(id) ? true : false);
27309     },
27310
27311
27312     // private
27313     handleMouseDBClick : function(e, t){
27314         
27315     },
27316     // private
27317     handleMouseDown : function(e, t)
27318     {
27319             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27320         if(this.isLocked() || rowIndex < 0 ){
27321             return;
27322         };
27323         if(e.shiftKey && this.last !== false){
27324             var last = this.last;
27325             this.selectRange(last, rowIndex, e.ctrlKey);
27326             this.last = last; // reset the last
27327             t.focus();
27328     
27329         }else{
27330             var isSelected = this.isSelected(rowIndex);
27331             //Roo.log("select row:" + rowIndex);
27332             if(isSelected){
27333                 this.deselectRow(rowIndex);
27334             } else {
27335                         this.selectRow(rowIndex, true);
27336             }
27337     
27338             /*
27339                 if(e.button !== 0 && isSelected){
27340                 alert('rowIndex 2: ' + rowIndex);
27341                     view.focusRow(rowIndex);
27342                 }else if(e.ctrlKey && isSelected){
27343                     this.deselectRow(rowIndex);
27344                 }else if(!isSelected){
27345                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27346                     view.focusRow(rowIndex);
27347                 }
27348             */
27349         }
27350         this.fireEvent("afterselectionchange", this);
27351     },
27352     // private
27353     handleDragableRowClick :  function(grid, rowIndex, e) 
27354     {
27355         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27356             this.selectRow(rowIndex, false);
27357             grid.view.focusRow(rowIndex);
27358              this.fireEvent("afterselectionchange", this);
27359         }
27360     },
27361     
27362     /**
27363      * Selects multiple rows.
27364      * @param {Array} rows Array of the indexes of the row to select
27365      * @param {Boolean} keepExisting (optional) True to keep existing selections
27366      */
27367     selectRows : function(rows, keepExisting){
27368         if(!keepExisting){
27369             this.clearSelections();
27370         }
27371         for(var i = 0, len = rows.length; i < len; i++){
27372             this.selectRow(rows[i], true);
27373         }
27374     },
27375
27376     /**
27377      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27378      * @param {Number} startRow The index of the first row in the range
27379      * @param {Number} endRow The index of the last row in the range
27380      * @param {Boolean} keepExisting (optional) True to retain existing selections
27381      */
27382     selectRange : function(startRow, endRow, keepExisting){
27383         if(this.locked) {
27384             return;
27385         }
27386         if(!keepExisting){
27387             this.clearSelections();
27388         }
27389         if(startRow <= endRow){
27390             for(var i = startRow; i <= endRow; i++){
27391                 this.selectRow(i, true);
27392             }
27393         }else{
27394             for(var i = startRow; i >= endRow; i--){
27395                 this.selectRow(i, true);
27396             }
27397         }
27398     },
27399
27400     /**
27401      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27402      * @param {Number} startRow The index of the first row in the range
27403      * @param {Number} endRow The index of the last row in the range
27404      */
27405     deselectRange : function(startRow, endRow, preventViewNotify){
27406         if(this.locked) {
27407             return;
27408         }
27409         for(var i = startRow; i <= endRow; i++){
27410             this.deselectRow(i, preventViewNotify);
27411         }
27412     },
27413
27414     /**
27415      * Selects a row.
27416      * @param {Number} row The index of the row to select
27417      * @param {Boolean} keepExisting (optional) True to keep existing selections
27418      */
27419     selectRow : function(index, keepExisting, preventViewNotify)
27420     {
27421             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27422             return;
27423         }
27424         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27425             if(!keepExisting || this.singleSelect){
27426                 this.clearSelections();
27427             }
27428             
27429             var r = this.grid.store.getAt(index);
27430             //console.log('selectRow - record id :' + r.id);
27431             
27432             this.selections.add(r);
27433             this.last = this.lastActive = index;
27434             if(!preventViewNotify){
27435                 var proxy = new Roo.Element(
27436                                 this.grid.getRowDom(index)
27437                 );
27438                 proxy.addClass('bg-info info');
27439             }
27440             this.fireEvent("rowselect", this, index, r);
27441             this.fireEvent("selectionchange", this);
27442         }
27443     },
27444
27445     /**
27446      * Deselects a row.
27447      * @param {Number} row The index of the row to deselect
27448      */
27449     deselectRow : function(index, preventViewNotify)
27450     {
27451         if(this.locked) {
27452             return;
27453         }
27454         if(this.last == index){
27455             this.last = false;
27456         }
27457         if(this.lastActive == index){
27458             this.lastActive = false;
27459         }
27460         
27461         var r = this.grid.store.getAt(index);
27462         if (!r) {
27463             return;
27464         }
27465         
27466         this.selections.remove(r);
27467         //.console.log('deselectRow - record id :' + r.id);
27468         if(!preventViewNotify){
27469         
27470             var proxy = new Roo.Element(
27471                 this.grid.getRowDom(index)
27472             );
27473             proxy.removeClass('bg-info info');
27474         }
27475         this.fireEvent("rowdeselect", this, index);
27476         this.fireEvent("selectionchange", this);
27477     },
27478
27479     // private
27480     restoreLast : function(){
27481         if(this._last){
27482             this.last = this._last;
27483         }
27484     },
27485
27486     // private
27487     acceptsNav : function(row, col, cm){
27488         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27489     },
27490
27491     // private
27492     onEditorKey : function(field, e){
27493         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27494         if(k == e.TAB){
27495             e.stopEvent();
27496             ed.completeEdit();
27497             if(e.shiftKey){
27498                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27499             }else{
27500                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27501             }
27502         }else if(k == e.ENTER && !e.ctrlKey){
27503             e.stopEvent();
27504             ed.completeEdit();
27505             if(e.shiftKey){
27506                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27507             }else{
27508                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27509             }
27510         }else if(k == e.ESC){
27511             ed.cancelEdit();
27512         }
27513         if(newCell){
27514             g.startEditing(newCell[0], newCell[1]);
27515         }
27516     }
27517 });
27518 /*
27519  * Based on:
27520  * Ext JS Library 1.1.1
27521  * Copyright(c) 2006-2007, Ext JS, LLC.
27522  *
27523  * Originally Released Under LGPL - original licence link has changed is not relivant.
27524  *
27525  * Fork - LGPL
27526  * <script type="text/javascript">
27527  */
27528  
27529 /**
27530  * @class Roo.bootstrap.PagingToolbar
27531  * @extends Roo.bootstrap.NavSimplebar
27532  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27533  * @constructor
27534  * Create a new PagingToolbar
27535  * @param {Object} config The config object
27536  * @param {Roo.data.Store} store
27537  */
27538 Roo.bootstrap.PagingToolbar = function(config)
27539 {
27540     // old args format still supported... - xtype is prefered..
27541         // created from xtype...
27542     
27543     this.ds = config.dataSource;
27544     
27545     if (config.store && !this.ds) {
27546         this.store= Roo.factory(config.store, Roo.data);
27547         this.ds = this.store;
27548         this.ds.xmodule = this.xmodule || false;
27549     }
27550     
27551     this.toolbarItems = [];
27552     if (config.items) {
27553         this.toolbarItems = config.items;
27554     }
27555     
27556     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27557     
27558     this.cursor = 0;
27559     
27560     if (this.ds) { 
27561         this.bind(this.ds);
27562     }
27563     
27564     if (Roo.bootstrap.version == 4) {
27565         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27566     } else {
27567         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27568     }
27569     
27570 };
27571
27572 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27573     /**
27574      * @cfg {Roo.data.Store} dataSource
27575      * The underlying data store providing the paged data
27576      */
27577     /**
27578      * @cfg {String/HTMLElement/Element} container
27579      * container The id or element that will contain the toolbar
27580      */
27581     /**
27582      * @cfg {Boolean} displayInfo
27583      * True to display the displayMsg (defaults to false)
27584      */
27585     /**
27586      * @cfg {Number} pageSize
27587      * The number of records to display per page (defaults to 20)
27588      */
27589     pageSize: 20,
27590     /**
27591      * @cfg {String} displayMsg
27592      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27593      */
27594     displayMsg : 'Displaying {0} - {1} of {2}',
27595     /**
27596      * @cfg {String} emptyMsg
27597      * The message to display when no records are found (defaults to "No data to display")
27598      */
27599     emptyMsg : 'No data to display',
27600     /**
27601      * Customizable piece of the default paging text (defaults to "Page")
27602      * @type String
27603      */
27604     beforePageText : "Page",
27605     /**
27606      * Customizable piece of the default paging text (defaults to "of %0")
27607      * @type String
27608      */
27609     afterPageText : "of {0}",
27610     /**
27611      * Customizable piece of the default paging text (defaults to "First Page")
27612      * @type String
27613      */
27614     firstText : "First Page",
27615     /**
27616      * Customizable piece of the default paging text (defaults to "Previous Page")
27617      * @type String
27618      */
27619     prevText : "Previous Page",
27620     /**
27621      * Customizable piece of the default paging text (defaults to "Next Page")
27622      * @type String
27623      */
27624     nextText : "Next Page",
27625     /**
27626      * Customizable piece of the default paging text (defaults to "Last Page")
27627      * @type String
27628      */
27629     lastText : "Last Page",
27630     /**
27631      * Customizable piece of the default paging text (defaults to "Refresh")
27632      * @type String
27633      */
27634     refreshText : "Refresh",
27635
27636     buttons : false,
27637     // private
27638     onRender : function(ct, position) 
27639     {
27640         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27641         this.navgroup.parentId = this.id;
27642         this.navgroup.onRender(this.el, null);
27643         // add the buttons to the navgroup
27644         
27645         if(this.displayInfo){
27646             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27647             this.displayEl = this.el.select('.x-paging-info', true).first();
27648 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27649 //            this.displayEl = navel.el.select('span',true).first();
27650         }
27651         
27652         var _this = this;
27653         
27654         if(this.buttons){
27655             Roo.each(_this.buttons, function(e){ // this might need to use render????
27656                Roo.factory(e).render(_this.el);
27657             });
27658         }
27659             
27660         Roo.each(_this.toolbarItems, function(e) {
27661             _this.navgroup.addItem(e);
27662         });
27663         
27664         
27665         this.first = this.navgroup.addItem({
27666             tooltip: this.firstText,
27667             cls: "prev btn-outline-secondary",
27668             html : ' <i class="fa fa-step-backward"></i>',
27669             disabled: true,
27670             preventDefault: true,
27671             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27672         });
27673         
27674         this.prev =  this.navgroup.addItem({
27675             tooltip: this.prevText,
27676             cls: "prev btn-outline-secondary",
27677             html : ' <i class="fa fa-backward"></i>',
27678             disabled: true,
27679             preventDefault: true,
27680             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27681         });
27682     //this.addSeparator();
27683         
27684         
27685         var field = this.navgroup.addItem( {
27686             tagtype : 'span',
27687             cls : 'x-paging-position  btn-outline-secondary',
27688              disabled: true,
27689             html : this.beforePageText  +
27690                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27691                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27692          } ); //?? escaped?
27693         
27694         this.field = field.el.select('input', true).first();
27695         this.field.on("keydown", this.onPagingKeydown, this);
27696         this.field.on("focus", function(){this.dom.select();});
27697     
27698     
27699         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27700         //this.field.setHeight(18);
27701         //this.addSeparator();
27702         this.next = this.navgroup.addItem({
27703             tooltip: this.nextText,
27704             cls: "next btn-outline-secondary",
27705             html : ' <i class="fa fa-forward"></i>',
27706             disabled: true,
27707             preventDefault: true,
27708             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27709         });
27710         this.last = this.navgroup.addItem({
27711             tooltip: this.lastText,
27712             html : ' <i class="fa fa-step-forward"></i>',
27713             cls: "next btn-outline-secondary",
27714             disabled: true,
27715             preventDefault: true,
27716             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27717         });
27718     //this.addSeparator();
27719         this.loading = this.navgroup.addItem({
27720             tooltip: this.refreshText,
27721             cls: "btn-outline-secondary",
27722             html : ' <i class="fa fa-refresh"></i>',
27723             preventDefault: true,
27724             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27725         });
27726         
27727     },
27728
27729     // private
27730     updateInfo : function(){
27731         if(this.displayEl){
27732             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27733             var msg = count == 0 ?
27734                 this.emptyMsg :
27735                 String.format(
27736                     this.displayMsg,
27737                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27738                 );
27739             this.displayEl.update(msg);
27740         }
27741     },
27742
27743     // private
27744     onLoad : function(ds, r, o)
27745     {
27746         this.cursor = o.params && o.params.start ? o.params.start : 0;
27747         
27748         var d = this.getPageData(),
27749             ap = d.activePage,
27750             ps = d.pages;
27751         
27752         
27753         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27754         this.field.dom.value = ap;
27755         this.first.setDisabled(ap == 1);
27756         this.prev.setDisabled(ap == 1);
27757         this.next.setDisabled(ap == ps);
27758         this.last.setDisabled(ap == ps);
27759         this.loading.enable();
27760         this.updateInfo();
27761     },
27762
27763     // private
27764     getPageData : function(){
27765         var total = this.ds.getTotalCount();
27766         return {
27767             total : total,
27768             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27769             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27770         };
27771     },
27772
27773     // private
27774     onLoadError : function(){
27775         this.loading.enable();
27776     },
27777
27778     // private
27779     onPagingKeydown : function(e){
27780         var k = e.getKey();
27781         var d = this.getPageData();
27782         if(k == e.RETURN){
27783             var v = this.field.dom.value, pageNum;
27784             if(!v || isNaN(pageNum = parseInt(v, 10))){
27785                 this.field.dom.value = d.activePage;
27786                 return;
27787             }
27788             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27789             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27790             e.stopEvent();
27791         }
27792         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))
27793         {
27794           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27795           this.field.dom.value = pageNum;
27796           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27797           e.stopEvent();
27798         }
27799         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27800         {
27801           var v = this.field.dom.value, pageNum; 
27802           var increment = (e.shiftKey) ? 10 : 1;
27803           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27804                 increment *= -1;
27805           }
27806           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27807             this.field.dom.value = d.activePage;
27808             return;
27809           }
27810           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27811           {
27812             this.field.dom.value = parseInt(v, 10) + increment;
27813             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27814             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27815           }
27816           e.stopEvent();
27817         }
27818     },
27819
27820     // private
27821     beforeLoad : function(){
27822         if(this.loading){
27823             this.loading.disable();
27824         }
27825     },
27826
27827     // private
27828     onClick : function(which){
27829         
27830         var ds = this.ds;
27831         if (!ds) {
27832             return;
27833         }
27834         
27835         switch(which){
27836             case "first":
27837                 ds.load({params:{start: 0, limit: this.pageSize}});
27838             break;
27839             case "prev":
27840                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27841             break;
27842             case "next":
27843                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27844             break;
27845             case "last":
27846                 var total = ds.getTotalCount();
27847                 var extra = total % this.pageSize;
27848                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27849                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27850             break;
27851             case "refresh":
27852                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27853             break;
27854         }
27855     },
27856
27857     /**
27858      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27859      * @param {Roo.data.Store} store The data store to unbind
27860      */
27861     unbind : function(ds){
27862         ds.un("beforeload", this.beforeLoad, this);
27863         ds.un("load", this.onLoad, this);
27864         ds.un("loadexception", this.onLoadError, this);
27865         ds.un("remove", this.updateInfo, this);
27866         ds.un("add", this.updateInfo, this);
27867         this.ds = undefined;
27868     },
27869
27870     /**
27871      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27872      * @param {Roo.data.Store} store The data store to bind
27873      */
27874     bind : function(ds){
27875         ds.on("beforeload", this.beforeLoad, this);
27876         ds.on("load", this.onLoad, this);
27877         ds.on("loadexception", this.onLoadError, this);
27878         ds.on("remove", this.updateInfo, this);
27879         ds.on("add", this.updateInfo, this);
27880         this.ds = ds;
27881     }
27882 });/*
27883  * - LGPL
27884  *
27885  * element
27886  * 
27887  */
27888
27889 /**
27890  * @class Roo.bootstrap.MessageBar
27891  * @extends Roo.bootstrap.Component
27892  * Bootstrap MessageBar class
27893  * @cfg {String} html contents of the MessageBar
27894  * @cfg {String} weight (info | success | warning | danger) default info
27895  * @cfg {String} beforeClass insert the bar before the given class
27896  * @cfg {Boolean} closable (true | false) default false
27897  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27898  * 
27899  * @constructor
27900  * Create a new Element
27901  * @param {Object} config The config object
27902  */
27903
27904 Roo.bootstrap.MessageBar = function(config){
27905     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27906 };
27907
27908 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27909     
27910     html: '',
27911     weight: 'info',
27912     closable: false,
27913     fixed: false,
27914     beforeClass: 'bootstrap-sticky-wrap',
27915     
27916     getAutoCreate : function(){
27917         
27918         var cfg = {
27919             tag: 'div',
27920             cls: 'alert alert-dismissable alert-' + this.weight,
27921             cn: [
27922                 {
27923                     tag: 'span',
27924                     cls: 'message',
27925                     html: this.html || ''
27926                 }
27927             ]
27928         };
27929         
27930         if(this.fixed){
27931             cfg.cls += ' alert-messages-fixed';
27932         }
27933         
27934         if(this.closable){
27935             cfg.cn.push({
27936                 tag: 'button',
27937                 cls: 'close',
27938                 html: 'x'
27939             });
27940         }
27941         
27942         return cfg;
27943     },
27944     
27945     onRender : function(ct, position)
27946     {
27947         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27948         
27949         if(!this.el){
27950             var cfg = Roo.apply({},  this.getAutoCreate());
27951             cfg.id = Roo.id();
27952             
27953             if (this.cls) {
27954                 cfg.cls += ' ' + this.cls;
27955             }
27956             if (this.style) {
27957                 cfg.style = this.style;
27958             }
27959             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27960             
27961             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27962         }
27963         
27964         this.el.select('>button.close').on('click', this.hide, this);
27965         
27966     },
27967     
27968     show : function()
27969     {
27970         if (!this.rendered) {
27971             this.render();
27972         }
27973         
27974         this.el.show();
27975         
27976         this.fireEvent('show', this);
27977         
27978     },
27979     
27980     hide : function()
27981     {
27982         if (!this.rendered) {
27983             this.render();
27984         }
27985         
27986         this.el.hide();
27987         
27988         this.fireEvent('hide', this);
27989     },
27990     
27991     update : function()
27992     {
27993 //        var e = this.el.dom.firstChild;
27994 //        
27995 //        if(this.closable){
27996 //            e = e.nextSibling;
27997 //        }
27998 //        
27999 //        e.data = this.html || '';
28000
28001         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28002     }
28003    
28004 });
28005
28006  
28007
28008      /*
28009  * - LGPL
28010  *
28011  * Graph
28012  * 
28013  */
28014
28015
28016 /**
28017  * @class Roo.bootstrap.Graph
28018  * @extends Roo.bootstrap.Component
28019  * Bootstrap Graph class
28020 > Prameters
28021  -sm {number} sm 4
28022  -md {number} md 5
28023  @cfg {String} graphtype  bar | vbar | pie
28024  @cfg {number} g_x coodinator | centre x (pie)
28025  @cfg {number} g_y coodinator | centre y (pie)
28026  @cfg {number} g_r radius (pie)
28027  @cfg {number} g_height height of the chart (respected by all elements in the set)
28028  @cfg {number} g_width width of the chart (respected by all elements in the set)
28029  @cfg {Object} title The title of the chart
28030     
28031  -{Array}  values
28032  -opts (object) options for the chart 
28033      o {
28034      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28035      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28036      o vgutter (number)
28037      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.
28038      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28039      o to
28040      o stretch (boolean)
28041      o }
28042  -opts (object) options for the pie
28043      o{
28044      o cut
28045      o startAngle (number)
28046      o endAngle (number)
28047      } 
28048  *
28049  * @constructor
28050  * Create a new Input
28051  * @param {Object} config The config object
28052  */
28053
28054 Roo.bootstrap.Graph = function(config){
28055     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28056     
28057     this.addEvents({
28058         // img events
28059         /**
28060          * @event click
28061          * The img click event for the img.
28062          * @param {Roo.EventObject} e
28063          */
28064         "click" : true
28065     });
28066 };
28067
28068 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28069     
28070     sm: 4,
28071     md: 5,
28072     graphtype: 'bar',
28073     g_height: 250,
28074     g_width: 400,
28075     g_x: 50,
28076     g_y: 50,
28077     g_r: 30,
28078     opts:{
28079         //g_colors: this.colors,
28080         g_type: 'soft',
28081         g_gutter: '20%'
28082
28083     },
28084     title : false,
28085
28086     getAutoCreate : function(){
28087         
28088         var cfg = {
28089             tag: 'div',
28090             html : null
28091         };
28092         
28093         
28094         return  cfg;
28095     },
28096
28097     onRender : function(ct,position){
28098         
28099         
28100         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28101         
28102         if (typeof(Raphael) == 'undefined') {
28103             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28104             return;
28105         }
28106         
28107         this.raphael = Raphael(this.el.dom);
28108         
28109                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28110                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28111                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28112                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28113                 /*
28114                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28115                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28116                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28117                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28118                 
28119                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28120                 r.barchart(330, 10, 300, 220, data1);
28121                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28122                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28123                 */
28124                 
28125                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28126                 // r.barchart(30, 30, 560, 250,  xdata, {
28127                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28128                 //     axis : "0 0 1 1",
28129                 //     axisxlabels :  xdata
28130                 //     //yvalues : cols,
28131                    
28132                 // });
28133 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28134 //        
28135 //        this.load(null,xdata,{
28136 //                axis : "0 0 1 1",
28137 //                axisxlabels :  xdata
28138 //                });
28139
28140     },
28141
28142     load : function(graphtype,xdata,opts)
28143     {
28144         this.raphael.clear();
28145         if(!graphtype) {
28146             graphtype = this.graphtype;
28147         }
28148         if(!opts){
28149             opts = this.opts;
28150         }
28151         var r = this.raphael,
28152             fin = function () {
28153                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28154             },
28155             fout = function () {
28156                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28157             },
28158             pfin = function() {
28159                 this.sector.stop();
28160                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28161
28162                 if (this.label) {
28163                     this.label[0].stop();
28164                     this.label[0].attr({ r: 7.5 });
28165                     this.label[1].attr({ "font-weight": 800 });
28166                 }
28167             },
28168             pfout = function() {
28169                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28170
28171                 if (this.label) {
28172                     this.label[0].animate({ r: 5 }, 500, "bounce");
28173                     this.label[1].attr({ "font-weight": 400 });
28174                 }
28175             };
28176
28177         switch(graphtype){
28178             case 'bar':
28179                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28180                 break;
28181             case 'hbar':
28182                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28183                 break;
28184             case 'pie':
28185 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28186 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28187 //            
28188                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28189                 
28190                 break;
28191
28192         }
28193         
28194         if(this.title){
28195             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28196         }
28197         
28198     },
28199     
28200     setTitle: function(o)
28201     {
28202         this.title = o;
28203     },
28204     
28205     initEvents: function() {
28206         
28207         if(!this.href){
28208             this.el.on('click', this.onClick, this);
28209         }
28210     },
28211     
28212     onClick : function(e)
28213     {
28214         Roo.log('img onclick');
28215         this.fireEvent('click', this, e);
28216     }
28217    
28218 });
28219
28220  
28221 /*
28222  * - LGPL
28223  *
28224  * numberBox
28225  * 
28226  */
28227 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28228
28229 /**
28230  * @class Roo.bootstrap.dash.NumberBox
28231  * @extends Roo.bootstrap.Component
28232  * Bootstrap NumberBox class
28233  * @cfg {String} headline Box headline
28234  * @cfg {String} content Box content
28235  * @cfg {String} icon Box icon
28236  * @cfg {String} footer Footer text
28237  * @cfg {String} fhref Footer href
28238  * 
28239  * @constructor
28240  * Create a new NumberBox
28241  * @param {Object} config The config object
28242  */
28243
28244
28245 Roo.bootstrap.dash.NumberBox = function(config){
28246     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28247     
28248 };
28249
28250 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28251     
28252     headline : '',
28253     content : '',
28254     icon : '',
28255     footer : '',
28256     fhref : '',
28257     ficon : '',
28258     
28259     getAutoCreate : function(){
28260         
28261         var cfg = {
28262             tag : 'div',
28263             cls : 'small-box ',
28264             cn : [
28265                 {
28266                     tag : 'div',
28267                     cls : 'inner',
28268                     cn :[
28269                         {
28270                             tag : 'h3',
28271                             cls : 'roo-headline',
28272                             html : this.headline
28273                         },
28274                         {
28275                             tag : 'p',
28276                             cls : 'roo-content',
28277                             html : this.content
28278                         }
28279                     ]
28280                 }
28281             ]
28282         };
28283         
28284         if(this.icon){
28285             cfg.cn.push({
28286                 tag : 'div',
28287                 cls : 'icon',
28288                 cn :[
28289                     {
28290                         tag : 'i',
28291                         cls : 'ion ' + this.icon
28292                     }
28293                 ]
28294             });
28295         }
28296         
28297         if(this.footer){
28298             var footer = {
28299                 tag : 'a',
28300                 cls : 'small-box-footer',
28301                 href : this.fhref || '#',
28302                 html : this.footer
28303             };
28304             
28305             cfg.cn.push(footer);
28306             
28307         }
28308         
28309         return  cfg;
28310     },
28311
28312     onRender : function(ct,position){
28313         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28314
28315
28316        
28317                 
28318     },
28319
28320     setHeadline: function (value)
28321     {
28322         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28323     },
28324     
28325     setFooter: function (value, href)
28326     {
28327         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28328         
28329         if(href){
28330             this.el.select('a.small-box-footer',true).first().attr('href', href);
28331         }
28332         
28333     },
28334
28335     setContent: function (value)
28336     {
28337         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28338     },
28339
28340     initEvents: function() 
28341     {   
28342         
28343     }
28344     
28345 });
28346
28347  
28348 /*
28349  * - LGPL
28350  *
28351  * TabBox
28352  * 
28353  */
28354 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28355
28356 /**
28357  * @class Roo.bootstrap.dash.TabBox
28358  * @extends Roo.bootstrap.Component
28359  * Bootstrap TabBox class
28360  * @cfg {String} title Title of the TabBox
28361  * @cfg {String} icon Icon of the TabBox
28362  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28363  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28364  * 
28365  * @constructor
28366  * Create a new TabBox
28367  * @param {Object} config The config object
28368  */
28369
28370
28371 Roo.bootstrap.dash.TabBox = function(config){
28372     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28373     this.addEvents({
28374         // raw events
28375         /**
28376          * @event addpane
28377          * When a pane is added
28378          * @param {Roo.bootstrap.dash.TabPane} pane
28379          */
28380         "addpane" : true,
28381         /**
28382          * @event activatepane
28383          * When a pane is activated
28384          * @param {Roo.bootstrap.dash.TabPane} pane
28385          */
28386         "activatepane" : true
28387         
28388          
28389     });
28390     
28391     this.panes = [];
28392 };
28393
28394 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28395
28396     title : '',
28397     icon : false,
28398     showtabs : true,
28399     tabScrollable : false,
28400     
28401     getChildContainer : function()
28402     {
28403         return this.el.select('.tab-content', true).first();
28404     },
28405     
28406     getAutoCreate : function(){
28407         
28408         var header = {
28409             tag: 'li',
28410             cls: 'pull-left header',
28411             html: this.title,
28412             cn : []
28413         };
28414         
28415         if(this.icon){
28416             header.cn.push({
28417                 tag: 'i',
28418                 cls: 'fa ' + this.icon
28419             });
28420         }
28421         
28422         var h = {
28423             tag: 'ul',
28424             cls: 'nav nav-tabs pull-right',
28425             cn: [
28426                 header
28427             ]
28428         };
28429         
28430         if(this.tabScrollable){
28431             h = {
28432                 tag: 'div',
28433                 cls: 'tab-header',
28434                 cn: [
28435                     {
28436                         tag: 'ul',
28437                         cls: 'nav nav-tabs pull-right',
28438                         cn: [
28439                             header
28440                         ]
28441                     }
28442                 ]
28443             };
28444         }
28445         
28446         var cfg = {
28447             tag: 'div',
28448             cls: 'nav-tabs-custom',
28449             cn: [
28450                 h,
28451                 {
28452                     tag: 'div',
28453                     cls: 'tab-content no-padding',
28454                     cn: []
28455                 }
28456             ]
28457         };
28458
28459         return  cfg;
28460     },
28461     initEvents : function()
28462     {
28463         //Roo.log('add add pane handler');
28464         this.on('addpane', this.onAddPane, this);
28465     },
28466      /**
28467      * Updates the box title
28468      * @param {String} html to set the title to.
28469      */
28470     setTitle : function(value)
28471     {
28472         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28473     },
28474     onAddPane : function(pane)
28475     {
28476         this.panes.push(pane);
28477         //Roo.log('addpane');
28478         //Roo.log(pane);
28479         // tabs are rendere left to right..
28480         if(!this.showtabs){
28481             return;
28482         }
28483         
28484         var ctr = this.el.select('.nav-tabs', true).first();
28485          
28486          
28487         var existing = ctr.select('.nav-tab',true);
28488         var qty = existing.getCount();;
28489         
28490         
28491         var tab = ctr.createChild({
28492             tag : 'li',
28493             cls : 'nav-tab' + (qty ? '' : ' active'),
28494             cn : [
28495                 {
28496                     tag : 'a',
28497                     href:'#',
28498                     html : pane.title
28499                 }
28500             ]
28501         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28502         pane.tab = tab;
28503         
28504         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28505         if (!qty) {
28506             pane.el.addClass('active');
28507         }
28508         
28509                 
28510     },
28511     onTabClick : function(ev,un,ob,pane)
28512     {
28513         //Roo.log('tab - prev default');
28514         ev.preventDefault();
28515         
28516         
28517         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28518         pane.tab.addClass('active');
28519         //Roo.log(pane.title);
28520         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28521         // technically we should have a deactivate event.. but maybe add later.
28522         // and it should not de-activate the selected tab...
28523         this.fireEvent('activatepane', pane);
28524         pane.el.addClass('active');
28525         pane.fireEvent('activate');
28526         
28527         
28528     },
28529     
28530     getActivePane : function()
28531     {
28532         var r = false;
28533         Roo.each(this.panes, function(p) {
28534             if(p.el.hasClass('active')){
28535                 r = p;
28536                 return false;
28537             }
28538             
28539             return;
28540         });
28541         
28542         return r;
28543     }
28544     
28545     
28546 });
28547
28548  
28549 /*
28550  * - LGPL
28551  *
28552  * Tab pane
28553  * 
28554  */
28555 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28556 /**
28557  * @class Roo.bootstrap.TabPane
28558  * @extends Roo.bootstrap.Component
28559  * Bootstrap TabPane class
28560  * @cfg {Boolean} active (false | true) Default false
28561  * @cfg {String} title title of panel
28562
28563  * 
28564  * @constructor
28565  * Create a new TabPane
28566  * @param {Object} config The config object
28567  */
28568
28569 Roo.bootstrap.dash.TabPane = function(config){
28570     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28571     
28572     this.addEvents({
28573         // raw events
28574         /**
28575          * @event activate
28576          * When a pane is activated
28577          * @param {Roo.bootstrap.dash.TabPane} pane
28578          */
28579         "activate" : true
28580          
28581     });
28582 };
28583
28584 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28585     
28586     active : false,
28587     title : '',
28588     
28589     // the tabBox that this is attached to.
28590     tab : false,
28591      
28592     getAutoCreate : function() 
28593     {
28594         var cfg = {
28595             tag: 'div',
28596             cls: 'tab-pane'
28597         };
28598         
28599         if(this.active){
28600             cfg.cls += ' active';
28601         }
28602         
28603         return cfg;
28604     },
28605     initEvents  : function()
28606     {
28607         //Roo.log('trigger add pane handler');
28608         this.parent().fireEvent('addpane', this)
28609     },
28610     
28611      /**
28612      * Updates the tab title 
28613      * @param {String} html to set the title to.
28614      */
28615     setTitle: function(str)
28616     {
28617         if (!this.tab) {
28618             return;
28619         }
28620         this.title = str;
28621         this.tab.select('a', true).first().dom.innerHTML = str;
28622         
28623     }
28624     
28625     
28626     
28627 });
28628
28629  
28630
28631
28632  /*
28633  * - LGPL
28634  *
28635  * menu
28636  * 
28637  */
28638 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28639
28640 /**
28641  * @class Roo.bootstrap.menu.Menu
28642  * @extends Roo.bootstrap.Component
28643  * Bootstrap Menu class - container for Menu
28644  * @cfg {String} html Text of the menu
28645  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28646  * @cfg {String} icon Font awesome icon
28647  * @cfg {String} pos Menu align to (top | bottom) default bottom
28648  * 
28649  * 
28650  * @constructor
28651  * Create a new Menu
28652  * @param {Object} config The config object
28653  */
28654
28655
28656 Roo.bootstrap.menu.Menu = function(config){
28657     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28658     
28659     this.addEvents({
28660         /**
28661          * @event beforeshow
28662          * Fires before this menu is displayed
28663          * @param {Roo.bootstrap.menu.Menu} this
28664          */
28665         beforeshow : true,
28666         /**
28667          * @event beforehide
28668          * Fires before this menu is hidden
28669          * @param {Roo.bootstrap.menu.Menu} this
28670          */
28671         beforehide : true,
28672         /**
28673          * @event show
28674          * Fires after this menu is displayed
28675          * @param {Roo.bootstrap.menu.Menu} this
28676          */
28677         show : true,
28678         /**
28679          * @event hide
28680          * Fires after this menu is hidden
28681          * @param {Roo.bootstrap.menu.Menu} this
28682          */
28683         hide : true,
28684         /**
28685          * @event click
28686          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28687          * @param {Roo.bootstrap.menu.Menu} this
28688          * @param {Roo.EventObject} e
28689          */
28690         click : true
28691     });
28692     
28693 };
28694
28695 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28696     
28697     submenu : false,
28698     html : '',
28699     weight : 'default',
28700     icon : false,
28701     pos : 'bottom',
28702     
28703     
28704     getChildContainer : function() {
28705         if(this.isSubMenu){
28706             return this.el;
28707         }
28708         
28709         return this.el.select('ul.dropdown-menu', true).first();  
28710     },
28711     
28712     getAutoCreate : function()
28713     {
28714         var text = [
28715             {
28716                 tag : 'span',
28717                 cls : 'roo-menu-text',
28718                 html : this.html
28719             }
28720         ];
28721         
28722         if(this.icon){
28723             text.unshift({
28724                 tag : 'i',
28725                 cls : 'fa ' + this.icon
28726             })
28727         }
28728         
28729         
28730         var cfg = {
28731             tag : 'div',
28732             cls : 'btn-group',
28733             cn : [
28734                 {
28735                     tag : 'button',
28736                     cls : 'dropdown-button btn btn-' + this.weight,
28737                     cn : text
28738                 },
28739                 {
28740                     tag : 'button',
28741                     cls : 'dropdown-toggle btn btn-' + this.weight,
28742                     cn : [
28743                         {
28744                             tag : 'span',
28745                             cls : 'caret'
28746                         }
28747                     ]
28748                 },
28749                 {
28750                     tag : 'ul',
28751                     cls : 'dropdown-menu'
28752                 }
28753             ]
28754             
28755         };
28756         
28757         if(this.pos == 'top'){
28758             cfg.cls += ' dropup';
28759         }
28760         
28761         if(this.isSubMenu){
28762             cfg = {
28763                 tag : 'ul',
28764                 cls : 'dropdown-menu'
28765             }
28766         }
28767         
28768         return cfg;
28769     },
28770     
28771     onRender : function(ct, position)
28772     {
28773         this.isSubMenu = ct.hasClass('dropdown-submenu');
28774         
28775         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28776     },
28777     
28778     initEvents : function() 
28779     {
28780         if(this.isSubMenu){
28781             return;
28782         }
28783         
28784         this.hidden = true;
28785         
28786         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28787         this.triggerEl.on('click', this.onTriggerPress, this);
28788         
28789         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28790         this.buttonEl.on('click', this.onClick, this);
28791         
28792     },
28793     
28794     list : function()
28795     {
28796         if(this.isSubMenu){
28797             return this.el;
28798         }
28799         
28800         return this.el.select('ul.dropdown-menu', true).first();
28801     },
28802     
28803     onClick : function(e)
28804     {
28805         this.fireEvent("click", this, e);
28806     },
28807     
28808     onTriggerPress  : function(e)
28809     {   
28810         if (this.isVisible()) {
28811             this.hide();
28812         } else {
28813             this.show();
28814         }
28815     },
28816     
28817     isVisible : function(){
28818         return !this.hidden;
28819     },
28820     
28821     show : function()
28822     {
28823         this.fireEvent("beforeshow", this);
28824         
28825         this.hidden = false;
28826         this.el.addClass('open');
28827         
28828         Roo.get(document).on("mouseup", this.onMouseUp, this);
28829         
28830         this.fireEvent("show", this);
28831         
28832         
28833     },
28834     
28835     hide : function()
28836     {
28837         this.fireEvent("beforehide", this);
28838         
28839         this.hidden = true;
28840         this.el.removeClass('open');
28841         
28842         Roo.get(document).un("mouseup", this.onMouseUp);
28843         
28844         this.fireEvent("hide", this);
28845     },
28846     
28847     onMouseUp : function()
28848     {
28849         this.hide();
28850     }
28851     
28852 });
28853
28854  
28855  /*
28856  * - LGPL
28857  *
28858  * menu item
28859  * 
28860  */
28861 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28862
28863 /**
28864  * @class Roo.bootstrap.menu.Item
28865  * @extends Roo.bootstrap.Component
28866  * Bootstrap MenuItem class
28867  * @cfg {Boolean} submenu (true | false) default false
28868  * @cfg {String} html text of the item
28869  * @cfg {String} href the link
28870  * @cfg {Boolean} disable (true | false) default false
28871  * @cfg {Boolean} preventDefault (true | false) default true
28872  * @cfg {String} icon Font awesome icon
28873  * @cfg {String} pos Submenu align to (left | right) default right 
28874  * 
28875  * 
28876  * @constructor
28877  * Create a new Item
28878  * @param {Object} config The config object
28879  */
28880
28881
28882 Roo.bootstrap.menu.Item = function(config){
28883     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28884     this.addEvents({
28885         /**
28886          * @event mouseover
28887          * Fires when the mouse is hovering over this menu
28888          * @param {Roo.bootstrap.menu.Item} this
28889          * @param {Roo.EventObject} e
28890          */
28891         mouseover : true,
28892         /**
28893          * @event mouseout
28894          * Fires when the mouse exits this menu
28895          * @param {Roo.bootstrap.menu.Item} this
28896          * @param {Roo.EventObject} e
28897          */
28898         mouseout : true,
28899         // raw events
28900         /**
28901          * @event click
28902          * The raw click event for the entire grid.
28903          * @param {Roo.EventObject} e
28904          */
28905         click : true
28906     });
28907 };
28908
28909 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28910     
28911     submenu : false,
28912     href : '',
28913     html : '',
28914     preventDefault: true,
28915     disable : false,
28916     icon : false,
28917     pos : 'right',
28918     
28919     getAutoCreate : function()
28920     {
28921         var text = [
28922             {
28923                 tag : 'span',
28924                 cls : 'roo-menu-item-text',
28925                 html : this.html
28926             }
28927         ];
28928         
28929         if(this.icon){
28930             text.unshift({
28931                 tag : 'i',
28932                 cls : 'fa ' + this.icon
28933             })
28934         }
28935         
28936         var cfg = {
28937             tag : 'li',
28938             cn : [
28939                 {
28940                     tag : 'a',
28941                     href : this.href || '#',
28942                     cn : text
28943                 }
28944             ]
28945         };
28946         
28947         if(this.disable){
28948             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28949         }
28950         
28951         if(this.submenu){
28952             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28953             
28954             if(this.pos == 'left'){
28955                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28956             }
28957         }
28958         
28959         return cfg;
28960     },
28961     
28962     initEvents : function() 
28963     {
28964         this.el.on('mouseover', this.onMouseOver, this);
28965         this.el.on('mouseout', this.onMouseOut, this);
28966         
28967         this.el.select('a', true).first().on('click', this.onClick, this);
28968         
28969     },
28970     
28971     onClick : function(e)
28972     {
28973         if(this.preventDefault){
28974             e.preventDefault();
28975         }
28976         
28977         this.fireEvent("click", this, e);
28978     },
28979     
28980     onMouseOver : function(e)
28981     {
28982         if(this.submenu && this.pos == 'left'){
28983             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28984         }
28985         
28986         this.fireEvent("mouseover", this, e);
28987     },
28988     
28989     onMouseOut : function(e)
28990     {
28991         this.fireEvent("mouseout", this, e);
28992     }
28993 });
28994
28995  
28996
28997  /*
28998  * - LGPL
28999  *
29000  * menu separator
29001  * 
29002  */
29003 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29004
29005 /**
29006  * @class Roo.bootstrap.menu.Separator
29007  * @extends Roo.bootstrap.Component
29008  * Bootstrap Separator class
29009  * 
29010  * @constructor
29011  * Create a new Separator
29012  * @param {Object} config The config object
29013  */
29014
29015
29016 Roo.bootstrap.menu.Separator = function(config){
29017     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29018 };
29019
29020 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29021     
29022     getAutoCreate : function(){
29023         var cfg = {
29024             tag : 'li',
29025             cls: 'dropdown-divider divider'
29026         };
29027         
29028         return cfg;
29029     }
29030    
29031 });
29032
29033  
29034
29035  /*
29036  * - LGPL
29037  *
29038  * Tooltip
29039  * 
29040  */
29041
29042 /**
29043  * @class Roo.bootstrap.Tooltip
29044  * Bootstrap Tooltip class
29045  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29046  * to determine which dom element triggers the tooltip.
29047  * 
29048  * It needs to add support for additional attributes like tooltip-position
29049  * 
29050  * @constructor
29051  * Create a new Toolti
29052  * @param {Object} config The config object
29053  */
29054
29055 Roo.bootstrap.Tooltip = function(config){
29056     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29057     
29058     this.alignment = Roo.bootstrap.Tooltip.alignment;
29059     
29060     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29061         this.alignment = config.alignment;
29062     }
29063     
29064 };
29065
29066 Roo.apply(Roo.bootstrap.Tooltip, {
29067     /**
29068      * @function init initialize tooltip monitoring.
29069      * @static
29070      */
29071     currentEl : false,
29072     currentTip : false,
29073     currentRegion : false,
29074     
29075     //  init : delay?
29076     
29077     init : function()
29078     {
29079         Roo.get(document).on('mouseover', this.enter ,this);
29080         Roo.get(document).on('mouseout', this.leave, this);
29081          
29082         
29083         this.currentTip = new Roo.bootstrap.Tooltip();
29084     },
29085     
29086     enter : function(ev)
29087     {
29088         var dom = ev.getTarget();
29089         
29090         //Roo.log(['enter',dom]);
29091         var el = Roo.fly(dom);
29092         if (this.currentEl) {
29093             //Roo.log(dom);
29094             //Roo.log(this.currentEl);
29095             //Roo.log(this.currentEl.contains(dom));
29096             if (this.currentEl == el) {
29097                 return;
29098             }
29099             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29100                 return;
29101             }
29102
29103         }
29104         
29105         if (this.currentTip.el) {
29106             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29107         }    
29108         //Roo.log(ev);
29109         
29110         if(!el || el.dom == document){
29111             return;
29112         }
29113         
29114         var bindEl = el; 
29115         var pel = false;
29116         if (!el.attr('tooltip')) {
29117             pel = el.findParent("[tooltip]");
29118             if (pel) {
29119                 bindEl = Roo.get(pel);
29120             }
29121         }
29122         
29123        
29124         
29125         // you can not look for children, as if el is the body.. then everythign is the child..
29126         if (!pel && !el.attr('tooltip')) { //
29127             if (!el.select("[tooltip]").elements.length) {
29128                 return;
29129             }
29130             // is the mouse over this child...?
29131             bindEl = el.select("[tooltip]").first();
29132             var xy = ev.getXY();
29133             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29134                 //Roo.log("not in region.");
29135                 return;
29136             }
29137             //Roo.log("child element over..");
29138             
29139         }
29140         this.currentEl = el;
29141         this.currentTip.bind(bindEl);
29142         this.currentRegion = Roo.lib.Region.getRegion(dom);
29143         this.currentTip.enter();
29144         
29145     },
29146     leave : function(ev)
29147     {
29148         var dom = ev.getTarget();
29149         //Roo.log(['leave',dom]);
29150         if (!this.currentEl) {
29151             return;
29152         }
29153         
29154         
29155         if (dom != this.currentEl.dom) {
29156             return;
29157         }
29158         var xy = ev.getXY();
29159         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29160             return;
29161         }
29162         // only activate leave if mouse cursor is outside... bounding box..
29163         
29164         
29165         
29166         
29167         if (this.currentTip) {
29168             this.currentTip.leave();
29169         }
29170         //Roo.log('clear currentEl');
29171         this.currentEl = false;
29172         
29173         
29174     },
29175     alignment : {
29176         'left' : ['r-l', [-2,0], 'right'],
29177         'right' : ['l-r', [2,0], 'left'],
29178         'bottom' : ['t-b', [0,2], 'top'],
29179         'top' : [ 'b-t', [0,-2], 'bottom']
29180     }
29181     
29182 });
29183
29184
29185 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29186     
29187     
29188     bindEl : false,
29189     
29190     delay : null, // can be { show : 300 , hide: 500}
29191     
29192     timeout : null,
29193     
29194     hoverState : null, //???
29195     
29196     placement : 'bottom', 
29197     
29198     alignment : false,
29199     
29200     getAutoCreate : function(){
29201     
29202         var cfg = {
29203            cls : 'tooltip',   
29204            role : 'tooltip',
29205            cn : [
29206                 {
29207                     cls : 'tooltip-arrow arrow'
29208                 },
29209                 {
29210                     cls : 'tooltip-inner'
29211                 }
29212            ]
29213         };
29214         
29215         return cfg;
29216     },
29217     bind : function(el)
29218     {
29219         this.bindEl = el;
29220     },
29221     
29222     initEvents : function()
29223     {
29224         this.arrowEl = this.el.select('.arrow', true).first();
29225         this.innerEl = this.el.select('.tooltip-inner', true).first();
29226     },
29227     
29228     enter : function () {
29229        
29230         if (this.timeout != null) {
29231             clearTimeout(this.timeout);
29232         }
29233         
29234         this.hoverState = 'in';
29235          //Roo.log("enter - show");
29236         if (!this.delay || !this.delay.show) {
29237             this.show();
29238             return;
29239         }
29240         var _t = this;
29241         this.timeout = setTimeout(function () {
29242             if (_t.hoverState == 'in') {
29243                 _t.show();
29244             }
29245         }, this.delay.show);
29246     },
29247     leave : function()
29248     {
29249         clearTimeout(this.timeout);
29250     
29251         this.hoverState = 'out';
29252          if (!this.delay || !this.delay.hide) {
29253             this.hide();
29254             return;
29255         }
29256        
29257         var _t = this;
29258         this.timeout = setTimeout(function () {
29259             //Roo.log("leave - timeout");
29260             
29261             if (_t.hoverState == 'out') {
29262                 _t.hide();
29263                 Roo.bootstrap.Tooltip.currentEl = false;
29264             }
29265         }, delay);
29266     },
29267     
29268     show : function (msg)
29269     {
29270         if (!this.el) {
29271             this.render(document.body);
29272         }
29273         // set content.
29274         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29275         
29276         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29277         
29278         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29279         
29280         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29281                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29282         
29283         var placement = typeof this.placement == 'function' ?
29284             this.placement.call(this, this.el, on_el) :
29285             this.placement;
29286             
29287         var autoToken = /\s?auto?\s?/i;
29288         var autoPlace = autoToken.test(placement);
29289         if (autoPlace) {
29290             placement = placement.replace(autoToken, '') || 'top';
29291         }
29292         
29293         //this.el.detach()
29294         //this.el.setXY([0,0]);
29295         this.el.show();
29296         //this.el.dom.style.display='block';
29297         
29298         //this.el.appendTo(on_el);
29299         
29300         var p = this.getPosition();
29301         var box = this.el.getBox();
29302         
29303         if (autoPlace) {
29304             // fixme..
29305         }
29306         
29307         var align = this.alignment[placement];
29308         
29309         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29310         
29311         if(placement == 'top' || placement == 'bottom'){
29312             if(xy[0] < 0){
29313                 placement = 'right';
29314             }
29315             
29316             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29317                 placement = 'left';
29318             }
29319             
29320             var scroll = Roo.select('body', true).first().getScroll();
29321             
29322             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29323                 placement = 'top';
29324             }
29325             
29326             align = this.alignment[placement];
29327             
29328             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29329             
29330         }
29331         
29332         var elems = document.getElementsByTagName('div');
29333         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29334         for (var i = 0; i < elems.length; i++) {
29335           var zindex = Number.parseInt(
29336                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29337                 10
29338           );
29339           if (zindex > highest) {
29340             highest = zindex;
29341           }
29342         }
29343         
29344         
29345         
29346         this.el.dom.style.zIndex = highest;
29347         
29348         this.el.alignTo(this.bindEl, align[0],align[1]);
29349         //var arrow = this.el.select('.arrow',true).first();
29350         //arrow.set(align[2], 
29351         
29352         this.el.addClass(placement);
29353         this.el.addClass("bs-tooltip-"+ placement);
29354         
29355         this.el.addClass('in fade show');
29356         
29357         this.hoverState = null;
29358         
29359         if (this.el.hasClass('fade')) {
29360             // fade it?
29361         }
29362         
29363         
29364         
29365         
29366         
29367     },
29368     hide : function()
29369     {
29370          
29371         if (!this.el) {
29372             return;
29373         }
29374         //this.el.setXY([0,0]);
29375         this.el.removeClass(['show', 'in']);
29376         //this.el.hide();
29377         
29378     }
29379     
29380 });
29381  
29382
29383  /*
29384  * - LGPL
29385  *
29386  * Location Picker
29387  * 
29388  */
29389
29390 /**
29391  * @class Roo.bootstrap.LocationPicker
29392  * @extends Roo.bootstrap.Component
29393  * Bootstrap LocationPicker class
29394  * @cfg {Number} latitude Position when init default 0
29395  * @cfg {Number} longitude Position when init default 0
29396  * @cfg {Number} zoom default 15
29397  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29398  * @cfg {Boolean} mapTypeControl default false
29399  * @cfg {Boolean} disableDoubleClickZoom default false
29400  * @cfg {Boolean} scrollwheel default true
29401  * @cfg {Boolean} streetViewControl default false
29402  * @cfg {Number} radius default 0
29403  * @cfg {String} locationName
29404  * @cfg {Boolean} draggable default true
29405  * @cfg {Boolean} enableAutocomplete default false
29406  * @cfg {Boolean} enableReverseGeocode default true
29407  * @cfg {String} markerTitle
29408  * 
29409  * @constructor
29410  * Create a new LocationPicker
29411  * @param {Object} config The config object
29412  */
29413
29414
29415 Roo.bootstrap.LocationPicker = function(config){
29416     
29417     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29418     
29419     this.addEvents({
29420         /**
29421          * @event initial
29422          * Fires when the picker initialized.
29423          * @param {Roo.bootstrap.LocationPicker} this
29424          * @param {Google Location} location
29425          */
29426         initial : true,
29427         /**
29428          * @event positionchanged
29429          * Fires when the picker position changed.
29430          * @param {Roo.bootstrap.LocationPicker} this
29431          * @param {Google Location} location
29432          */
29433         positionchanged : true,
29434         /**
29435          * @event resize
29436          * Fires when the map resize.
29437          * @param {Roo.bootstrap.LocationPicker} this
29438          */
29439         resize : true,
29440         /**
29441          * @event show
29442          * Fires when the map show.
29443          * @param {Roo.bootstrap.LocationPicker} this
29444          */
29445         show : true,
29446         /**
29447          * @event hide
29448          * Fires when the map hide.
29449          * @param {Roo.bootstrap.LocationPicker} this
29450          */
29451         hide : true,
29452         /**
29453          * @event mapClick
29454          * Fires when click the map.
29455          * @param {Roo.bootstrap.LocationPicker} this
29456          * @param {Map event} e
29457          */
29458         mapClick : true,
29459         /**
29460          * @event mapRightClick
29461          * Fires when right click the map.
29462          * @param {Roo.bootstrap.LocationPicker} this
29463          * @param {Map event} e
29464          */
29465         mapRightClick : true,
29466         /**
29467          * @event markerClick
29468          * Fires when click the marker.
29469          * @param {Roo.bootstrap.LocationPicker} this
29470          * @param {Map event} e
29471          */
29472         markerClick : true,
29473         /**
29474          * @event markerRightClick
29475          * Fires when right click the marker.
29476          * @param {Roo.bootstrap.LocationPicker} this
29477          * @param {Map event} e
29478          */
29479         markerRightClick : true,
29480         /**
29481          * @event OverlayViewDraw
29482          * Fires when OverlayView Draw
29483          * @param {Roo.bootstrap.LocationPicker} this
29484          */
29485         OverlayViewDraw : true,
29486         /**
29487          * @event OverlayViewOnAdd
29488          * Fires when OverlayView Draw
29489          * @param {Roo.bootstrap.LocationPicker} this
29490          */
29491         OverlayViewOnAdd : true,
29492         /**
29493          * @event OverlayViewOnRemove
29494          * Fires when OverlayView Draw
29495          * @param {Roo.bootstrap.LocationPicker} this
29496          */
29497         OverlayViewOnRemove : true,
29498         /**
29499          * @event OverlayViewShow
29500          * Fires when OverlayView Draw
29501          * @param {Roo.bootstrap.LocationPicker} this
29502          * @param {Pixel} cpx
29503          */
29504         OverlayViewShow : true,
29505         /**
29506          * @event OverlayViewHide
29507          * Fires when OverlayView Draw
29508          * @param {Roo.bootstrap.LocationPicker} this
29509          */
29510         OverlayViewHide : true,
29511         /**
29512          * @event loadexception
29513          * Fires when load google lib failed.
29514          * @param {Roo.bootstrap.LocationPicker} this
29515          */
29516         loadexception : true
29517     });
29518         
29519 };
29520
29521 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29522     
29523     gMapContext: false,
29524     
29525     latitude: 0,
29526     longitude: 0,
29527     zoom: 15,
29528     mapTypeId: false,
29529     mapTypeControl: false,
29530     disableDoubleClickZoom: false,
29531     scrollwheel: true,
29532     streetViewControl: false,
29533     radius: 0,
29534     locationName: '',
29535     draggable: true,
29536     enableAutocomplete: false,
29537     enableReverseGeocode: true,
29538     markerTitle: '',
29539     
29540     getAutoCreate: function()
29541     {
29542
29543         var cfg = {
29544             tag: 'div',
29545             cls: 'roo-location-picker'
29546         };
29547         
29548         return cfg
29549     },
29550     
29551     initEvents: function(ct, position)
29552     {       
29553         if(!this.el.getWidth() || this.isApplied()){
29554             return;
29555         }
29556         
29557         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29558         
29559         this.initial();
29560     },
29561     
29562     initial: function()
29563     {
29564         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29565             this.fireEvent('loadexception', this);
29566             return;
29567         }
29568         
29569         if(!this.mapTypeId){
29570             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29571         }
29572         
29573         this.gMapContext = this.GMapContext();
29574         
29575         this.initOverlayView();
29576         
29577         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29578         
29579         var _this = this;
29580                 
29581         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29582             _this.setPosition(_this.gMapContext.marker.position);
29583         });
29584         
29585         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29586             _this.fireEvent('mapClick', this, event);
29587             
29588         });
29589
29590         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29591             _this.fireEvent('mapRightClick', this, event);
29592             
29593         });
29594         
29595         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29596             _this.fireEvent('markerClick', this, event);
29597             
29598         });
29599
29600         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29601             _this.fireEvent('markerRightClick', this, event);
29602             
29603         });
29604         
29605         this.setPosition(this.gMapContext.location);
29606         
29607         this.fireEvent('initial', this, this.gMapContext.location);
29608     },
29609     
29610     initOverlayView: function()
29611     {
29612         var _this = this;
29613         
29614         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29615             
29616             draw: function()
29617             {
29618                 _this.fireEvent('OverlayViewDraw', _this);
29619             },
29620             
29621             onAdd: function()
29622             {
29623                 _this.fireEvent('OverlayViewOnAdd', _this);
29624             },
29625             
29626             onRemove: function()
29627             {
29628                 _this.fireEvent('OverlayViewOnRemove', _this);
29629             },
29630             
29631             show: function(cpx)
29632             {
29633                 _this.fireEvent('OverlayViewShow', _this, cpx);
29634             },
29635             
29636             hide: function()
29637             {
29638                 _this.fireEvent('OverlayViewHide', _this);
29639             }
29640             
29641         });
29642     },
29643     
29644     fromLatLngToContainerPixel: function(event)
29645     {
29646         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29647     },
29648     
29649     isApplied: function() 
29650     {
29651         return this.getGmapContext() == false ? false : true;
29652     },
29653     
29654     getGmapContext: function() 
29655     {
29656         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29657     },
29658     
29659     GMapContext: function() 
29660     {
29661         var position = new google.maps.LatLng(this.latitude, this.longitude);
29662         
29663         var _map = new google.maps.Map(this.el.dom, {
29664             center: position,
29665             zoom: this.zoom,
29666             mapTypeId: this.mapTypeId,
29667             mapTypeControl: this.mapTypeControl,
29668             disableDoubleClickZoom: this.disableDoubleClickZoom,
29669             scrollwheel: this.scrollwheel,
29670             streetViewControl: this.streetViewControl,
29671             locationName: this.locationName,
29672             draggable: this.draggable,
29673             enableAutocomplete: this.enableAutocomplete,
29674             enableReverseGeocode: this.enableReverseGeocode
29675         });
29676         
29677         var _marker = new google.maps.Marker({
29678             position: position,
29679             map: _map,
29680             title: this.markerTitle,
29681             draggable: this.draggable
29682         });
29683         
29684         return {
29685             map: _map,
29686             marker: _marker,
29687             circle: null,
29688             location: position,
29689             radius: this.radius,
29690             locationName: this.locationName,
29691             addressComponents: {
29692                 formatted_address: null,
29693                 addressLine1: null,
29694                 addressLine2: null,
29695                 streetName: null,
29696                 streetNumber: null,
29697                 city: null,
29698                 district: null,
29699                 state: null,
29700                 stateOrProvince: null
29701             },
29702             settings: this,
29703             domContainer: this.el.dom,
29704             geodecoder: new google.maps.Geocoder()
29705         };
29706     },
29707     
29708     drawCircle: function(center, radius, options) 
29709     {
29710         if (this.gMapContext.circle != null) {
29711             this.gMapContext.circle.setMap(null);
29712         }
29713         if (radius > 0) {
29714             radius *= 1;
29715             options = Roo.apply({}, options, {
29716                 strokeColor: "#0000FF",
29717                 strokeOpacity: .35,
29718                 strokeWeight: 2,
29719                 fillColor: "#0000FF",
29720                 fillOpacity: .2
29721             });
29722             
29723             options.map = this.gMapContext.map;
29724             options.radius = radius;
29725             options.center = center;
29726             this.gMapContext.circle = new google.maps.Circle(options);
29727             return this.gMapContext.circle;
29728         }
29729         
29730         return null;
29731     },
29732     
29733     setPosition: function(location) 
29734     {
29735         this.gMapContext.location = location;
29736         this.gMapContext.marker.setPosition(location);
29737         this.gMapContext.map.panTo(location);
29738         this.drawCircle(location, this.gMapContext.radius, {});
29739         
29740         var _this = this;
29741         
29742         if (this.gMapContext.settings.enableReverseGeocode) {
29743             this.gMapContext.geodecoder.geocode({
29744                 latLng: this.gMapContext.location
29745             }, function(results, status) {
29746                 
29747                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29748                     _this.gMapContext.locationName = results[0].formatted_address;
29749                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29750                     
29751                     _this.fireEvent('positionchanged', this, location);
29752                 }
29753             });
29754             
29755             return;
29756         }
29757         
29758         this.fireEvent('positionchanged', this, location);
29759     },
29760     
29761     resize: function()
29762     {
29763         google.maps.event.trigger(this.gMapContext.map, "resize");
29764         
29765         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29766         
29767         this.fireEvent('resize', this);
29768     },
29769     
29770     setPositionByLatLng: function(latitude, longitude)
29771     {
29772         this.setPosition(new google.maps.LatLng(latitude, longitude));
29773     },
29774     
29775     getCurrentPosition: function() 
29776     {
29777         return {
29778             latitude: this.gMapContext.location.lat(),
29779             longitude: this.gMapContext.location.lng()
29780         };
29781     },
29782     
29783     getAddressName: function() 
29784     {
29785         return this.gMapContext.locationName;
29786     },
29787     
29788     getAddressComponents: function() 
29789     {
29790         return this.gMapContext.addressComponents;
29791     },
29792     
29793     address_component_from_google_geocode: function(address_components) 
29794     {
29795         var result = {};
29796         
29797         for (var i = 0; i < address_components.length; i++) {
29798             var component = address_components[i];
29799             if (component.types.indexOf("postal_code") >= 0) {
29800                 result.postalCode = component.short_name;
29801             } else if (component.types.indexOf("street_number") >= 0) {
29802                 result.streetNumber = component.short_name;
29803             } else if (component.types.indexOf("route") >= 0) {
29804                 result.streetName = component.short_name;
29805             } else if (component.types.indexOf("neighborhood") >= 0) {
29806                 result.city = component.short_name;
29807             } else if (component.types.indexOf("locality") >= 0) {
29808                 result.city = component.short_name;
29809             } else if (component.types.indexOf("sublocality") >= 0) {
29810                 result.district = component.short_name;
29811             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29812                 result.stateOrProvince = component.short_name;
29813             } else if (component.types.indexOf("country") >= 0) {
29814                 result.country = component.short_name;
29815             }
29816         }
29817         
29818         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29819         result.addressLine2 = "";
29820         return result;
29821     },
29822     
29823     setZoomLevel: function(zoom)
29824     {
29825         this.gMapContext.map.setZoom(zoom);
29826     },
29827     
29828     show: function()
29829     {
29830         if(!this.el){
29831             return;
29832         }
29833         
29834         this.el.show();
29835         
29836         this.resize();
29837         
29838         this.fireEvent('show', this);
29839     },
29840     
29841     hide: function()
29842     {
29843         if(!this.el){
29844             return;
29845         }
29846         
29847         this.el.hide();
29848         
29849         this.fireEvent('hide', this);
29850     }
29851     
29852 });
29853
29854 Roo.apply(Roo.bootstrap.LocationPicker, {
29855     
29856     OverlayView : function(map, options)
29857     {
29858         options = options || {};
29859         
29860         this.setMap(map);
29861     }
29862     
29863     
29864 });/**
29865  * @class Roo.bootstrap.Alert
29866  * @extends Roo.bootstrap.Component
29867  * Bootstrap Alert class - shows an alert area box
29868  * eg
29869  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29870   Enter a valid email address
29871 </div>
29872  * @licence LGPL
29873  * @cfg {String} title The title of alert
29874  * @cfg {String} html The content of alert
29875  * @cfg {String} weight (  success | info | warning | danger )
29876  * @cfg {String} fa font-awesomeicon
29877  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29878  * @cfg {Boolean} close true to show a x closer
29879  * 
29880  * 
29881  * @constructor
29882  * Create a new alert
29883  * @param {Object} config The config object
29884  */
29885
29886
29887 Roo.bootstrap.Alert = function(config){
29888     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29889     
29890 };
29891
29892 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29893     
29894     title: '',
29895     html: '',
29896     weight: false,
29897     fa: false,
29898     faicon: false, // BC
29899     close : false,
29900     
29901     
29902     getAutoCreate : function()
29903     {
29904         
29905         var cfg = {
29906             tag : 'div',
29907             cls : 'alert',
29908             cn : [
29909                 {
29910                     tag: 'button',
29911                     type :  "button",
29912                     cls: "close",
29913                     html : '×',
29914                     style : this.close ? '' : 'display:none'
29915                 },
29916                 {
29917                     tag : 'i',
29918                     cls : 'roo-alert-icon'
29919                     
29920                 },
29921                 {
29922                     tag : 'b',
29923                     cls : 'roo-alert-title',
29924                     html : this.title
29925                 },
29926                 {
29927                     tag : 'span',
29928                     cls : 'roo-alert-text',
29929                     html : this.html
29930                 }
29931             ]
29932         };
29933         
29934         if(this.faicon){
29935             cfg.cn[0].cls += ' fa ' + this.faicon;
29936         }
29937         if(this.fa){
29938             cfg.cn[0].cls += ' fa ' + this.fa;
29939         }
29940         
29941         if(this.weight){
29942             cfg.cls += ' alert-' + this.weight;
29943         }
29944         
29945         return cfg;
29946     },
29947     
29948     initEvents: function() 
29949     {
29950         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29951         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29952         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29953         if (this.seconds > 0) {
29954             this.hide.defer(this.seconds, this);
29955         }
29956     },
29957     
29958     setTitle : function(str)
29959     {
29960         this.titleEl.dom.innerHTML = str;
29961     },
29962     
29963     setText : function(str)
29964     {
29965         this.titleEl.dom.innerHTML = str;
29966     },
29967     
29968     setWeight : function(weight)
29969     {
29970         if(this.weight){
29971             this.el.removeClass('alert-' + this.weight);
29972         }
29973         
29974         this.weight = weight;
29975         
29976         this.el.addClass('alert-' + this.weight);
29977     },
29978     
29979     setIcon : function(icon)
29980     {
29981         if(this.faicon){
29982             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29983         }
29984         
29985         this.faicon = icon;
29986         
29987         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29988     },
29989     
29990     hide: function() 
29991     {
29992         this.el.hide();   
29993     },
29994     
29995     show: function() 
29996     {  
29997         this.el.show();   
29998     }
29999     
30000 });
30001
30002  
30003 /*
30004 * Licence: LGPL
30005 */
30006
30007 /**
30008  * @class Roo.bootstrap.UploadCropbox
30009  * @extends Roo.bootstrap.Component
30010  * Bootstrap UploadCropbox class
30011  * @cfg {String} emptyText show when image has been loaded
30012  * @cfg {String} rotateNotify show when image too small to rotate
30013  * @cfg {Number} errorTimeout default 3000
30014  * @cfg {Number} minWidth default 300
30015  * @cfg {Number} minHeight default 300
30016  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30017  * @cfg {Boolean} isDocument (true|false) default false
30018  * @cfg {String} url action url
30019  * @cfg {String} paramName default 'imageUpload'
30020  * @cfg {String} method default POST
30021  * @cfg {Boolean} loadMask (true|false) default true
30022  * @cfg {Boolean} loadingText default 'Loading...'
30023  * 
30024  * @constructor
30025  * Create a new UploadCropbox
30026  * @param {Object} config The config object
30027  */
30028
30029 Roo.bootstrap.UploadCropbox = function(config){
30030     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30031     
30032     this.addEvents({
30033         /**
30034          * @event beforeselectfile
30035          * Fire before select file
30036          * @param {Roo.bootstrap.UploadCropbox} this
30037          */
30038         "beforeselectfile" : true,
30039         /**
30040          * @event initial
30041          * Fire after initEvent
30042          * @param {Roo.bootstrap.UploadCropbox} this
30043          */
30044         "initial" : true,
30045         /**
30046          * @event crop
30047          * Fire after initEvent
30048          * @param {Roo.bootstrap.UploadCropbox} this
30049          * @param {String} data
30050          */
30051         "crop" : true,
30052         /**
30053          * @event prepare
30054          * Fire when preparing the file data
30055          * @param {Roo.bootstrap.UploadCropbox} this
30056          * @param {Object} file
30057          */
30058         "prepare" : true,
30059         /**
30060          * @event exception
30061          * Fire when get exception
30062          * @param {Roo.bootstrap.UploadCropbox} this
30063          * @param {XMLHttpRequest} xhr
30064          */
30065         "exception" : true,
30066         /**
30067          * @event beforeloadcanvas
30068          * Fire before load the canvas
30069          * @param {Roo.bootstrap.UploadCropbox} this
30070          * @param {String} src
30071          */
30072         "beforeloadcanvas" : true,
30073         /**
30074          * @event trash
30075          * Fire when trash image
30076          * @param {Roo.bootstrap.UploadCropbox} this
30077          */
30078         "trash" : true,
30079         /**
30080          * @event download
30081          * Fire when download the image
30082          * @param {Roo.bootstrap.UploadCropbox} this
30083          */
30084         "download" : true,
30085         /**
30086          * @event footerbuttonclick
30087          * Fire when footerbuttonclick
30088          * @param {Roo.bootstrap.UploadCropbox} this
30089          * @param {String} type
30090          */
30091         "footerbuttonclick" : true,
30092         /**
30093          * @event resize
30094          * Fire when resize
30095          * @param {Roo.bootstrap.UploadCropbox} this
30096          */
30097         "resize" : true,
30098         /**
30099          * @event rotate
30100          * Fire when rotate the image
30101          * @param {Roo.bootstrap.UploadCropbox} this
30102          * @param {String} pos
30103          */
30104         "rotate" : true,
30105         /**
30106          * @event inspect
30107          * Fire when inspect the file
30108          * @param {Roo.bootstrap.UploadCropbox} this
30109          * @param {Object} file
30110          */
30111         "inspect" : true,
30112         /**
30113          * @event upload
30114          * Fire when xhr upload the file
30115          * @param {Roo.bootstrap.UploadCropbox} this
30116          * @param {Object} data
30117          */
30118         "upload" : true,
30119         /**
30120          * @event arrange
30121          * Fire when arrange the file data
30122          * @param {Roo.bootstrap.UploadCropbox} this
30123          * @param {Object} formData
30124          */
30125         "arrange" : true
30126     });
30127     
30128     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30129 };
30130
30131 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30132     
30133     emptyText : 'Click to upload image',
30134     rotateNotify : 'Image is too small to rotate',
30135     errorTimeout : 3000,
30136     scale : 0,
30137     baseScale : 1,
30138     rotate : 0,
30139     dragable : false,
30140     pinching : false,
30141     mouseX : 0,
30142     mouseY : 0,
30143     cropData : false,
30144     minWidth : 300,
30145     minHeight : 300,
30146     file : false,
30147     exif : {},
30148     baseRotate : 1,
30149     cropType : 'image/jpeg',
30150     buttons : false,
30151     canvasLoaded : false,
30152     isDocument : false,
30153     method : 'POST',
30154     paramName : 'imageUpload',
30155     loadMask : true,
30156     loadingText : 'Loading...',
30157     maskEl : false,
30158     
30159     getAutoCreate : function()
30160     {
30161         var cfg = {
30162             tag : 'div',
30163             cls : 'roo-upload-cropbox',
30164             cn : [
30165                 {
30166                     tag : 'input',
30167                     cls : 'roo-upload-cropbox-selector',
30168                     type : 'file'
30169                 },
30170                 {
30171                     tag : 'div',
30172                     cls : 'roo-upload-cropbox-body',
30173                     style : 'cursor:pointer',
30174                     cn : [
30175                         {
30176                             tag : 'div',
30177                             cls : 'roo-upload-cropbox-preview'
30178                         },
30179                         {
30180                             tag : 'div',
30181                             cls : 'roo-upload-cropbox-thumb'
30182                         },
30183                         {
30184                             tag : 'div',
30185                             cls : 'roo-upload-cropbox-empty-notify',
30186                             html : this.emptyText
30187                         },
30188                         {
30189                             tag : 'div',
30190                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30191                             html : this.rotateNotify
30192                         }
30193                     ]
30194                 },
30195                 {
30196                     tag : 'div',
30197                     cls : 'roo-upload-cropbox-footer',
30198                     cn : {
30199                         tag : 'div',
30200                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30201                         cn : []
30202                     }
30203                 }
30204             ]
30205         };
30206         
30207         return cfg;
30208     },
30209     
30210     onRender : function(ct, position)
30211     {
30212         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30213         
30214         if (this.buttons.length) {
30215             
30216             Roo.each(this.buttons, function(bb) {
30217                 
30218                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30219                 
30220                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30221                 
30222             }, this);
30223         }
30224         
30225         if(this.loadMask){
30226             this.maskEl = this.el;
30227         }
30228     },
30229     
30230     initEvents : function()
30231     {
30232         this.urlAPI = (window.createObjectURL && window) || 
30233                                 (window.URL && URL.revokeObjectURL && URL) || 
30234                                 (window.webkitURL && webkitURL);
30235                         
30236         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30237         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30238         
30239         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30240         this.selectorEl.hide();
30241         
30242         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30243         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30244         
30245         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30246         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30247         this.thumbEl.hide();
30248         
30249         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30250         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30251         
30252         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30253         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254         this.errorEl.hide();
30255         
30256         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30257         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30258         this.footerEl.hide();
30259         
30260         this.setThumbBoxSize();
30261         
30262         this.bind();
30263         
30264         this.resize();
30265         
30266         this.fireEvent('initial', this);
30267     },
30268
30269     bind : function()
30270     {
30271         var _this = this;
30272         
30273         window.addEventListener("resize", function() { _this.resize(); } );
30274         
30275         this.bodyEl.on('click', this.beforeSelectFile, this);
30276         
30277         if(Roo.isTouch){
30278             this.bodyEl.on('touchstart', this.onTouchStart, this);
30279             this.bodyEl.on('touchmove', this.onTouchMove, this);
30280             this.bodyEl.on('touchend', this.onTouchEnd, this);
30281         }
30282         
30283         if(!Roo.isTouch){
30284             this.bodyEl.on('mousedown', this.onMouseDown, this);
30285             this.bodyEl.on('mousemove', this.onMouseMove, this);
30286             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30287             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30288             Roo.get(document).on('mouseup', this.onMouseUp, this);
30289         }
30290         
30291         this.selectorEl.on('change', this.onFileSelected, this);
30292     },
30293     
30294     reset : function()
30295     {    
30296         this.scale = 0;
30297         this.baseScale = 1;
30298         this.rotate = 0;
30299         this.baseRotate = 1;
30300         this.dragable = false;
30301         this.pinching = false;
30302         this.mouseX = 0;
30303         this.mouseY = 0;
30304         this.cropData = false;
30305         this.notifyEl.dom.innerHTML = this.emptyText;
30306         
30307         this.selectorEl.dom.value = '';
30308         
30309     },
30310     
30311     resize : function()
30312     {
30313         if(this.fireEvent('resize', this) != false){
30314             this.setThumbBoxPosition();
30315             this.setCanvasPosition();
30316         }
30317     },
30318     
30319     onFooterButtonClick : function(e, el, o, type)
30320     {
30321         switch (type) {
30322             case 'rotate-left' :
30323                 this.onRotateLeft(e);
30324                 break;
30325             case 'rotate-right' :
30326                 this.onRotateRight(e);
30327                 break;
30328             case 'picture' :
30329                 this.beforeSelectFile(e);
30330                 break;
30331             case 'trash' :
30332                 this.trash(e);
30333                 break;
30334             case 'crop' :
30335                 this.crop(e);
30336                 break;
30337             case 'download' :
30338                 this.download(e);
30339                 break;
30340             default :
30341                 break;
30342         }
30343         
30344         this.fireEvent('footerbuttonclick', this, type);
30345     },
30346     
30347     beforeSelectFile : function(e)
30348     {
30349         e.preventDefault();
30350         
30351         if(this.fireEvent('beforeselectfile', this) != false){
30352             this.selectorEl.dom.click();
30353         }
30354     },
30355     
30356     onFileSelected : function(e)
30357     {
30358         e.preventDefault();
30359         
30360         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30361             return;
30362         }
30363         
30364         var file = this.selectorEl.dom.files[0];
30365         
30366         if(this.fireEvent('inspect', this, file) != false){
30367             this.prepare(file);
30368         }
30369         
30370     },
30371     
30372     trash : function(e)
30373     {
30374         this.fireEvent('trash', this);
30375     },
30376     
30377     download : function(e)
30378     {
30379         this.fireEvent('download', this);
30380     },
30381     
30382     loadCanvas : function(src)
30383     {   
30384         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30385             
30386             this.reset();
30387             
30388             this.imageEl = document.createElement('img');
30389             
30390             var _this = this;
30391             
30392             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30393             
30394             this.imageEl.src = src;
30395         }
30396     },
30397     
30398     onLoadCanvas : function()
30399     {   
30400         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30401         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30402         
30403         this.bodyEl.un('click', this.beforeSelectFile, this);
30404         
30405         this.notifyEl.hide();
30406         this.thumbEl.show();
30407         this.footerEl.show();
30408         
30409         this.baseRotateLevel();
30410         
30411         if(this.isDocument){
30412             this.setThumbBoxSize();
30413         }
30414         
30415         this.setThumbBoxPosition();
30416         
30417         this.baseScaleLevel();
30418         
30419         this.draw();
30420         
30421         this.resize();
30422         
30423         this.canvasLoaded = true;
30424         
30425         if(this.loadMask){
30426             this.maskEl.unmask();
30427         }
30428         
30429     },
30430     
30431     setCanvasPosition : function()
30432     {   
30433         if(!this.canvasEl){
30434             return;
30435         }
30436         
30437         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30438         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30439         
30440         this.previewEl.setLeft(pw);
30441         this.previewEl.setTop(ph);
30442         
30443     },
30444     
30445     onMouseDown : function(e)
30446     {   
30447         e.stopEvent();
30448         
30449         this.dragable = true;
30450         this.pinching = false;
30451         
30452         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30453             this.dragable = false;
30454             return;
30455         }
30456         
30457         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30458         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30459         
30460     },
30461     
30462     onMouseMove : function(e)
30463     {   
30464         e.stopEvent();
30465         
30466         if(!this.canvasLoaded){
30467             return;
30468         }
30469         
30470         if (!this.dragable){
30471             return;
30472         }
30473         
30474         var minX = Math.ceil(this.thumbEl.getLeft(true));
30475         var minY = Math.ceil(this.thumbEl.getTop(true));
30476         
30477         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30478         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30479         
30480         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30481         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30482         
30483         x = x - this.mouseX;
30484         y = y - this.mouseY;
30485         
30486         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30487         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30488         
30489         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30490         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30491         
30492         this.previewEl.setLeft(bgX);
30493         this.previewEl.setTop(bgY);
30494         
30495         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30496         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30497     },
30498     
30499     onMouseUp : function(e)
30500     {   
30501         e.stopEvent();
30502         
30503         this.dragable = false;
30504     },
30505     
30506     onMouseWheel : function(e)
30507     {   
30508         e.stopEvent();
30509         
30510         this.startScale = this.scale;
30511         
30512         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30513         
30514         if(!this.zoomable()){
30515             this.scale = this.startScale;
30516             return;
30517         }
30518         
30519         this.draw();
30520         
30521         return;
30522     },
30523     
30524     zoomable : function()
30525     {
30526         var minScale = this.thumbEl.getWidth() / this.minWidth;
30527         
30528         if(this.minWidth < this.minHeight){
30529             minScale = this.thumbEl.getHeight() / this.minHeight;
30530         }
30531         
30532         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30533         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30534         
30535         if(
30536                 this.isDocument &&
30537                 (this.rotate == 0 || this.rotate == 180) && 
30538                 (
30539                     width > this.imageEl.OriginWidth || 
30540                     height > this.imageEl.OriginHeight ||
30541                     (width < this.minWidth && height < this.minHeight)
30542                 )
30543         ){
30544             return false;
30545         }
30546         
30547         if(
30548                 this.isDocument &&
30549                 (this.rotate == 90 || this.rotate == 270) && 
30550                 (
30551                     width > this.imageEl.OriginWidth || 
30552                     height > this.imageEl.OriginHeight ||
30553                     (width < this.minHeight && height < this.minWidth)
30554                 )
30555         ){
30556             return false;
30557         }
30558         
30559         if(
30560                 !this.isDocument &&
30561                 (this.rotate == 0 || this.rotate == 180) && 
30562                 (
30563                     width < this.minWidth || 
30564                     width > this.imageEl.OriginWidth || 
30565                     height < this.minHeight || 
30566                     height > this.imageEl.OriginHeight
30567                 )
30568         ){
30569             return false;
30570         }
30571         
30572         if(
30573                 !this.isDocument &&
30574                 (this.rotate == 90 || this.rotate == 270) && 
30575                 (
30576                     width < this.minHeight || 
30577                     width > this.imageEl.OriginWidth || 
30578                     height < this.minWidth || 
30579                     height > this.imageEl.OriginHeight
30580                 )
30581         ){
30582             return false;
30583         }
30584         
30585         return true;
30586         
30587     },
30588     
30589     onRotateLeft : function(e)
30590     {   
30591         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30592             
30593             var minScale = this.thumbEl.getWidth() / this.minWidth;
30594             
30595             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30596             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30597             
30598             this.startScale = this.scale;
30599             
30600             while (this.getScaleLevel() < minScale){
30601             
30602                 this.scale = this.scale + 1;
30603                 
30604                 if(!this.zoomable()){
30605                     break;
30606                 }
30607                 
30608                 if(
30609                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30610                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30611                 ){
30612                     continue;
30613                 }
30614                 
30615                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30616
30617                 this.draw();
30618                 
30619                 return;
30620             }
30621             
30622             this.scale = this.startScale;
30623             
30624             this.onRotateFail();
30625             
30626             return false;
30627         }
30628         
30629         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30630
30631         if(this.isDocument){
30632             this.setThumbBoxSize();
30633             this.setThumbBoxPosition();
30634             this.setCanvasPosition();
30635         }
30636         
30637         this.draw();
30638         
30639         this.fireEvent('rotate', this, 'left');
30640         
30641     },
30642     
30643     onRotateRight : function(e)
30644     {
30645         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30646             
30647             var minScale = this.thumbEl.getWidth() / this.minWidth;
30648         
30649             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30650             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30651             
30652             this.startScale = this.scale;
30653             
30654             while (this.getScaleLevel() < minScale){
30655             
30656                 this.scale = this.scale + 1;
30657                 
30658                 if(!this.zoomable()){
30659                     break;
30660                 }
30661                 
30662                 if(
30663                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30664                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30665                 ){
30666                     continue;
30667                 }
30668                 
30669                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30670
30671                 this.draw();
30672                 
30673                 return;
30674             }
30675             
30676             this.scale = this.startScale;
30677             
30678             this.onRotateFail();
30679             
30680             return false;
30681         }
30682         
30683         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30684
30685         if(this.isDocument){
30686             this.setThumbBoxSize();
30687             this.setThumbBoxPosition();
30688             this.setCanvasPosition();
30689         }
30690         
30691         this.draw();
30692         
30693         this.fireEvent('rotate', this, 'right');
30694     },
30695     
30696     onRotateFail : function()
30697     {
30698         this.errorEl.show(true);
30699         
30700         var _this = this;
30701         
30702         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30703     },
30704     
30705     draw : function()
30706     {
30707         this.previewEl.dom.innerHTML = '';
30708         
30709         var canvasEl = document.createElement("canvas");
30710         
30711         var contextEl = canvasEl.getContext("2d");
30712         
30713         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30714         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30715         var center = this.imageEl.OriginWidth / 2;
30716         
30717         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30718             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30719             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30720             center = this.imageEl.OriginHeight / 2;
30721         }
30722         
30723         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30724         
30725         contextEl.translate(center, center);
30726         contextEl.rotate(this.rotate * Math.PI / 180);
30727
30728         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30729         
30730         this.canvasEl = document.createElement("canvas");
30731         
30732         this.contextEl = this.canvasEl.getContext("2d");
30733         
30734         switch (this.rotate) {
30735             case 0 :
30736                 
30737                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30738                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30739                 
30740                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30741                 
30742                 break;
30743             case 90 : 
30744                 
30745                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30746                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30747                 
30748                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30749                     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);
30750                     break;
30751                 }
30752                 
30753                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30754                 
30755                 break;
30756             case 180 :
30757                 
30758                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30759                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30760                 
30761                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30762                     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);
30763                     break;
30764                 }
30765                 
30766                 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);
30767                 
30768                 break;
30769             case 270 :
30770                 
30771                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30772                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30773         
30774                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30775                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30776                     break;
30777                 }
30778                 
30779                 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);
30780                 
30781                 break;
30782             default : 
30783                 break;
30784         }
30785         
30786         this.previewEl.appendChild(this.canvasEl);
30787         
30788         this.setCanvasPosition();
30789     },
30790     
30791     crop : function()
30792     {
30793         if(!this.canvasLoaded){
30794             return;
30795         }
30796         
30797         var imageCanvas = document.createElement("canvas");
30798         
30799         var imageContext = imageCanvas.getContext("2d");
30800         
30801         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30802         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30803         
30804         var center = imageCanvas.width / 2;
30805         
30806         imageContext.translate(center, center);
30807         
30808         imageContext.rotate(this.rotate * Math.PI / 180);
30809         
30810         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30811         
30812         var canvas = document.createElement("canvas");
30813         
30814         var context = canvas.getContext("2d");
30815                 
30816         canvas.width = this.minWidth;
30817         canvas.height = this.minHeight;
30818
30819         switch (this.rotate) {
30820             case 0 :
30821                 
30822                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30823                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30824                 
30825                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30826                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30827                 
30828                 var targetWidth = this.minWidth - 2 * x;
30829                 var targetHeight = this.minHeight - 2 * y;
30830                 
30831                 var scale = 1;
30832                 
30833                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30834                     scale = targetWidth / width;
30835                 }
30836                 
30837                 if(x > 0 && y == 0){
30838                     scale = targetHeight / height;
30839                 }
30840                 
30841                 if(x > 0 && y > 0){
30842                     scale = targetWidth / width;
30843                     
30844                     if(width < height){
30845                         scale = targetHeight / height;
30846                     }
30847                 }
30848                 
30849                 context.scale(scale, scale);
30850                 
30851                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30852                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30853
30854                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30855                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30856
30857                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30858                 
30859                 break;
30860             case 90 : 
30861                 
30862                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30863                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30864                 
30865                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30866                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30867                 
30868                 var targetWidth = this.minWidth - 2 * x;
30869                 var targetHeight = this.minHeight - 2 * y;
30870                 
30871                 var scale = 1;
30872                 
30873                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30874                     scale = targetWidth / width;
30875                 }
30876                 
30877                 if(x > 0 && y == 0){
30878                     scale = targetHeight / height;
30879                 }
30880                 
30881                 if(x > 0 && y > 0){
30882                     scale = targetWidth / width;
30883                     
30884                     if(width < height){
30885                         scale = targetHeight / height;
30886                     }
30887                 }
30888                 
30889                 context.scale(scale, scale);
30890                 
30891                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30892                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30893
30894                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30895                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30896                 
30897                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30898                 
30899                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30900                 
30901                 break;
30902             case 180 :
30903                 
30904                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30905                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30906                 
30907                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30908                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30909                 
30910                 var targetWidth = this.minWidth - 2 * x;
30911                 var targetHeight = this.minHeight - 2 * y;
30912                 
30913                 var scale = 1;
30914                 
30915                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30916                     scale = targetWidth / width;
30917                 }
30918                 
30919                 if(x > 0 && y == 0){
30920                     scale = targetHeight / height;
30921                 }
30922                 
30923                 if(x > 0 && y > 0){
30924                     scale = targetWidth / width;
30925                     
30926                     if(width < height){
30927                         scale = targetHeight / height;
30928                     }
30929                 }
30930                 
30931                 context.scale(scale, scale);
30932                 
30933                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30934                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30935
30936                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30937                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30938
30939                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30940                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30941                 
30942                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30943                 
30944                 break;
30945             case 270 :
30946                 
30947                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30948                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30949                 
30950                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30951                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30952                 
30953                 var targetWidth = this.minWidth - 2 * x;
30954                 var targetHeight = this.minHeight - 2 * y;
30955                 
30956                 var scale = 1;
30957                 
30958                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30959                     scale = targetWidth / width;
30960                 }
30961                 
30962                 if(x > 0 && y == 0){
30963                     scale = targetHeight / height;
30964                 }
30965                 
30966                 if(x > 0 && y > 0){
30967                     scale = targetWidth / width;
30968                     
30969                     if(width < height){
30970                         scale = targetHeight / height;
30971                     }
30972                 }
30973                 
30974                 context.scale(scale, scale);
30975                 
30976                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30977                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30978
30979                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30980                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30981                 
30982                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30983                 
30984                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30985                 
30986                 break;
30987             default : 
30988                 break;
30989         }
30990         
30991         this.cropData = canvas.toDataURL(this.cropType);
30992         
30993         if(this.fireEvent('crop', this, this.cropData) !== false){
30994             this.process(this.file, this.cropData);
30995         }
30996         
30997         return;
30998         
30999     },
31000     
31001     setThumbBoxSize : function()
31002     {
31003         var width, height;
31004         
31005         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31006             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31007             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31008             
31009             this.minWidth = width;
31010             this.minHeight = height;
31011             
31012             if(this.rotate == 90 || this.rotate == 270){
31013                 this.minWidth = height;
31014                 this.minHeight = width;
31015             }
31016         }
31017         
31018         height = 300;
31019         width = Math.ceil(this.minWidth * height / this.minHeight);
31020         
31021         if(this.minWidth > this.minHeight){
31022             width = 300;
31023             height = Math.ceil(this.minHeight * width / this.minWidth);
31024         }
31025         
31026         this.thumbEl.setStyle({
31027             width : width + 'px',
31028             height : height + 'px'
31029         });
31030
31031         return;
31032             
31033     },
31034     
31035     setThumbBoxPosition : function()
31036     {
31037         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31038         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31039         
31040         this.thumbEl.setLeft(x);
31041         this.thumbEl.setTop(y);
31042         
31043     },
31044     
31045     baseRotateLevel : function()
31046     {
31047         this.baseRotate = 1;
31048         
31049         if(
31050                 typeof(this.exif) != 'undefined' &&
31051                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31052                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31053         ){
31054             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31055         }
31056         
31057         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31058         
31059     },
31060     
31061     baseScaleLevel : function()
31062     {
31063         var width, height;
31064         
31065         if(this.isDocument){
31066             
31067             if(this.baseRotate == 6 || this.baseRotate == 8){
31068             
31069                 height = this.thumbEl.getHeight();
31070                 this.baseScale = height / this.imageEl.OriginWidth;
31071
31072                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31073                     width = this.thumbEl.getWidth();
31074                     this.baseScale = width / this.imageEl.OriginHeight;
31075                 }
31076
31077                 return;
31078             }
31079
31080             height = this.thumbEl.getHeight();
31081             this.baseScale = height / this.imageEl.OriginHeight;
31082
31083             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31084                 width = this.thumbEl.getWidth();
31085                 this.baseScale = width / this.imageEl.OriginWidth;
31086             }
31087
31088             return;
31089         }
31090         
31091         if(this.baseRotate == 6 || this.baseRotate == 8){
31092             
31093             width = this.thumbEl.getHeight();
31094             this.baseScale = width / this.imageEl.OriginHeight;
31095             
31096             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31097                 height = this.thumbEl.getWidth();
31098                 this.baseScale = height / this.imageEl.OriginHeight;
31099             }
31100             
31101             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31102                 height = this.thumbEl.getWidth();
31103                 this.baseScale = height / this.imageEl.OriginHeight;
31104                 
31105                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31106                     width = this.thumbEl.getHeight();
31107                     this.baseScale = width / this.imageEl.OriginWidth;
31108                 }
31109             }
31110             
31111             return;
31112         }
31113         
31114         width = this.thumbEl.getWidth();
31115         this.baseScale = width / this.imageEl.OriginWidth;
31116         
31117         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31118             height = this.thumbEl.getHeight();
31119             this.baseScale = height / this.imageEl.OriginHeight;
31120         }
31121         
31122         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31123             
31124             height = this.thumbEl.getHeight();
31125             this.baseScale = height / this.imageEl.OriginHeight;
31126             
31127             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31128                 width = this.thumbEl.getWidth();
31129                 this.baseScale = width / this.imageEl.OriginWidth;
31130             }
31131             
31132         }
31133         
31134         return;
31135     },
31136     
31137     getScaleLevel : function()
31138     {
31139         return this.baseScale * Math.pow(1.1, this.scale);
31140     },
31141     
31142     onTouchStart : function(e)
31143     {
31144         if(!this.canvasLoaded){
31145             this.beforeSelectFile(e);
31146             return;
31147         }
31148         
31149         var touches = e.browserEvent.touches;
31150         
31151         if(!touches){
31152             return;
31153         }
31154         
31155         if(touches.length == 1){
31156             this.onMouseDown(e);
31157             return;
31158         }
31159         
31160         if(touches.length != 2){
31161             return;
31162         }
31163         
31164         var coords = [];
31165         
31166         for(var i = 0, finger; finger = touches[i]; i++){
31167             coords.push(finger.pageX, finger.pageY);
31168         }
31169         
31170         var x = Math.pow(coords[0] - coords[2], 2);
31171         var y = Math.pow(coords[1] - coords[3], 2);
31172         
31173         this.startDistance = Math.sqrt(x + y);
31174         
31175         this.startScale = this.scale;
31176         
31177         this.pinching = true;
31178         this.dragable = false;
31179         
31180     },
31181     
31182     onTouchMove : function(e)
31183     {
31184         if(!this.pinching && !this.dragable){
31185             return;
31186         }
31187         
31188         var touches = e.browserEvent.touches;
31189         
31190         if(!touches){
31191             return;
31192         }
31193         
31194         if(this.dragable){
31195             this.onMouseMove(e);
31196             return;
31197         }
31198         
31199         var coords = [];
31200         
31201         for(var i = 0, finger; finger = touches[i]; i++){
31202             coords.push(finger.pageX, finger.pageY);
31203         }
31204         
31205         var x = Math.pow(coords[0] - coords[2], 2);
31206         var y = Math.pow(coords[1] - coords[3], 2);
31207         
31208         this.endDistance = Math.sqrt(x + y);
31209         
31210         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31211         
31212         if(!this.zoomable()){
31213             this.scale = this.startScale;
31214             return;
31215         }
31216         
31217         this.draw();
31218         
31219     },
31220     
31221     onTouchEnd : function(e)
31222     {
31223         this.pinching = false;
31224         this.dragable = false;
31225         
31226     },
31227     
31228     process : function(file, crop)
31229     {
31230         if(this.loadMask){
31231             this.maskEl.mask(this.loadingText);
31232         }
31233         
31234         this.xhr = new XMLHttpRequest();
31235         
31236         file.xhr = this.xhr;
31237
31238         this.xhr.open(this.method, this.url, true);
31239         
31240         var headers = {
31241             "Accept": "application/json",
31242             "Cache-Control": "no-cache",
31243             "X-Requested-With": "XMLHttpRequest"
31244         };
31245         
31246         for (var headerName in headers) {
31247             var headerValue = headers[headerName];
31248             if (headerValue) {
31249                 this.xhr.setRequestHeader(headerName, headerValue);
31250             }
31251         }
31252         
31253         var _this = this;
31254         
31255         this.xhr.onload = function()
31256         {
31257             _this.xhrOnLoad(_this.xhr);
31258         }
31259         
31260         this.xhr.onerror = function()
31261         {
31262             _this.xhrOnError(_this.xhr);
31263         }
31264         
31265         var formData = new FormData();
31266
31267         formData.append('returnHTML', 'NO');
31268         
31269         if(crop){
31270             formData.append('crop', crop);
31271         }
31272         
31273         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31274             formData.append(this.paramName, file, file.name);
31275         }
31276         
31277         if(typeof(file.filename) != 'undefined'){
31278             formData.append('filename', file.filename);
31279         }
31280         
31281         if(typeof(file.mimetype) != 'undefined'){
31282             formData.append('mimetype', file.mimetype);
31283         }
31284         
31285         if(this.fireEvent('arrange', this, formData) != false){
31286             this.xhr.send(formData);
31287         };
31288     },
31289     
31290     xhrOnLoad : function(xhr)
31291     {
31292         if(this.loadMask){
31293             this.maskEl.unmask();
31294         }
31295         
31296         if (xhr.readyState !== 4) {
31297             this.fireEvent('exception', this, xhr);
31298             return;
31299         }
31300
31301         var response = Roo.decode(xhr.responseText);
31302         
31303         if(!response.success){
31304             this.fireEvent('exception', this, xhr);
31305             return;
31306         }
31307         
31308         var response = Roo.decode(xhr.responseText);
31309         
31310         this.fireEvent('upload', this, response);
31311         
31312     },
31313     
31314     xhrOnError : function()
31315     {
31316         if(this.loadMask){
31317             this.maskEl.unmask();
31318         }
31319         
31320         Roo.log('xhr on error');
31321         
31322         var response = Roo.decode(xhr.responseText);
31323           
31324         Roo.log(response);
31325         
31326     },
31327     
31328     prepare : function(file)
31329     {   
31330         if(this.loadMask){
31331             this.maskEl.mask(this.loadingText);
31332         }
31333         
31334         this.file = false;
31335         this.exif = {};
31336         
31337         if(typeof(file) === 'string'){
31338             this.loadCanvas(file);
31339             return;
31340         }
31341         
31342         if(!file || !this.urlAPI){
31343             return;
31344         }
31345         
31346         this.file = file;
31347         this.cropType = file.type;
31348         
31349         var _this = this;
31350         
31351         if(this.fireEvent('prepare', this, this.file) != false){
31352             
31353             var reader = new FileReader();
31354             
31355             reader.onload = function (e) {
31356                 if (e.target.error) {
31357                     Roo.log(e.target.error);
31358                     return;
31359                 }
31360                 
31361                 var buffer = e.target.result,
31362                     dataView = new DataView(buffer),
31363                     offset = 2,
31364                     maxOffset = dataView.byteLength - 4,
31365                     markerBytes,
31366                     markerLength;
31367                 
31368                 if (dataView.getUint16(0) === 0xffd8) {
31369                     while (offset < maxOffset) {
31370                         markerBytes = dataView.getUint16(offset);
31371                         
31372                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31373                             markerLength = dataView.getUint16(offset + 2) + 2;
31374                             if (offset + markerLength > dataView.byteLength) {
31375                                 Roo.log('Invalid meta data: Invalid segment size.');
31376                                 break;
31377                             }
31378                             
31379                             if(markerBytes == 0xffe1){
31380                                 _this.parseExifData(
31381                                     dataView,
31382                                     offset,
31383                                     markerLength
31384                                 );
31385                             }
31386                             
31387                             offset += markerLength;
31388                             
31389                             continue;
31390                         }
31391                         
31392                         break;
31393                     }
31394                     
31395                 }
31396                 
31397                 var url = _this.urlAPI.createObjectURL(_this.file);
31398                 
31399                 _this.loadCanvas(url);
31400                 
31401                 return;
31402             }
31403             
31404             reader.readAsArrayBuffer(this.file);
31405             
31406         }
31407         
31408     },
31409     
31410     parseExifData : function(dataView, offset, length)
31411     {
31412         var tiffOffset = offset + 10,
31413             littleEndian,
31414             dirOffset;
31415     
31416         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31417             // No Exif data, might be XMP data instead
31418             return;
31419         }
31420         
31421         // Check for the ASCII code for "Exif" (0x45786966):
31422         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31423             // No Exif data, might be XMP data instead
31424             return;
31425         }
31426         if (tiffOffset + 8 > dataView.byteLength) {
31427             Roo.log('Invalid Exif data: Invalid segment size.');
31428             return;
31429         }
31430         // Check for the two null bytes:
31431         if (dataView.getUint16(offset + 8) !== 0x0000) {
31432             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31433             return;
31434         }
31435         // Check the byte alignment:
31436         switch (dataView.getUint16(tiffOffset)) {
31437         case 0x4949:
31438             littleEndian = true;
31439             break;
31440         case 0x4D4D:
31441             littleEndian = false;
31442             break;
31443         default:
31444             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31445             return;
31446         }
31447         // Check for the TIFF tag marker (0x002A):
31448         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31449             Roo.log('Invalid Exif data: Missing TIFF marker.');
31450             return;
31451         }
31452         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31453         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31454         
31455         this.parseExifTags(
31456             dataView,
31457             tiffOffset,
31458             tiffOffset + dirOffset,
31459             littleEndian
31460         );
31461     },
31462     
31463     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31464     {
31465         var tagsNumber,
31466             dirEndOffset,
31467             i;
31468         if (dirOffset + 6 > dataView.byteLength) {
31469             Roo.log('Invalid Exif data: Invalid directory offset.');
31470             return;
31471         }
31472         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31473         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31474         if (dirEndOffset + 4 > dataView.byteLength) {
31475             Roo.log('Invalid Exif data: Invalid directory size.');
31476             return;
31477         }
31478         for (i = 0; i < tagsNumber; i += 1) {
31479             this.parseExifTag(
31480                 dataView,
31481                 tiffOffset,
31482                 dirOffset + 2 + 12 * i, // tag offset
31483                 littleEndian
31484             );
31485         }
31486         // Return the offset to the next directory:
31487         return dataView.getUint32(dirEndOffset, littleEndian);
31488     },
31489     
31490     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31491     {
31492         var tag = dataView.getUint16(offset, littleEndian);
31493         
31494         this.exif[tag] = this.getExifValue(
31495             dataView,
31496             tiffOffset,
31497             offset,
31498             dataView.getUint16(offset + 2, littleEndian), // tag type
31499             dataView.getUint32(offset + 4, littleEndian), // tag length
31500             littleEndian
31501         );
31502     },
31503     
31504     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31505     {
31506         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31507             tagSize,
31508             dataOffset,
31509             values,
31510             i,
31511             str,
31512             c;
31513     
31514         if (!tagType) {
31515             Roo.log('Invalid Exif data: Invalid tag type.');
31516             return;
31517         }
31518         
31519         tagSize = tagType.size * length;
31520         // Determine if the value is contained in the dataOffset bytes,
31521         // or if the value at the dataOffset is a pointer to the actual data:
31522         dataOffset = tagSize > 4 ?
31523                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31524         if (dataOffset + tagSize > dataView.byteLength) {
31525             Roo.log('Invalid Exif data: Invalid data offset.');
31526             return;
31527         }
31528         if (length === 1) {
31529             return tagType.getValue(dataView, dataOffset, littleEndian);
31530         }
31531         values = [];
31532         for (i = 0; i < length; i += 1) {
31533             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31534         }
31535         
31536         if (tagType.ascii) {
31537             str = '';
31538             // Concatenate the chars:
31539             for (i = 0; i < values.length; i += 1) {
31540                 c = values[i];
31541                 // Ignore the terminating NULL byte(s):
31542                 if (c === '\u0000') {
31543                     break;
31544                 }
31545                 str += c;
31546             }
31547             return str;
31548         }
31549         return values;
31550     }
31551     
31552 });
31553
31554 Roo.apply(Roo.bootstrap.UploadCropbox, {
31555     tags : {
31556         'Orientation': 0x0112
31557     },
31558     
31559     Orientation: {
31560             1: 0, //'top-left',
31561 //            2: 'top-right',
31562             3: 180, //'bottom-right',
31563 //            4: 'bottom-left',
31564 //            5: 'left-top',
31565             6: 90, //'right-top',
31566 //            7: 'right-bottom',
31567             8: 270 //'left-bottom'
31568     },
31569     
31570     exifTagTypes : {
31571         // byte, 8-bit unsigned int:
31572         1: {
31573             getValue: function (dataView, dataOffset) {
31574                 return dataView.getUint8(dataOffset);
31575             },
31576             size: 1
31577         },
31578         // ascii, 8-bit byte:
31579         2: {
31580             getValue: function (dataView, dataOffset) {
31581                 return String.fromCharCode(dataView.getUint8(dataOffset));
31582             },
31583             size: 1,
31584             ascii: true
31585         },
31586         // short, 16 bit int:
31587         3: {
31588             getValue: function (dataView, dataOffset, littleEndian) {
31589                 return dataView.getUint16(dataOffset, littleEndian);
31590             },
31591             size: 2
31592         },
31593         // long, 32 bit int:
31594         4: {
31595             getValue: function (dataView, dataOffset, littleEndian) {
31596                 return dataView.getUint32(dataOffset, littleEndian);
31597             },
31598             size: 4
31599         },
31600         // rational = two long values, first is numerator, second is denominator:
31601         5: {
31602             getValue: function (dataView, dataOffset, littleEndian) {
31603                 return dataView.getUint32(dataOffset, littleEndian) /
31604                     dataView.getUint32(dataOffset + 4, littleEndian);
31605             },
31606             size: 8
31607         },
31608         // slong, 32 bit signed int:
31609         9: {
31610             getValue: function (dataView, dataOffset, littleEndian) {
31611                 return dataView.getInt32(dataOffset, littleEndian);
31612             },
31613             size: 4
31614         },
31615         // srational, two slongs, first is numerator, second is denominator:
31616         10: {
31617             getValue: function (dataView, dataOffset, littleEndian) {
31618                 return dataView.getInt32(dataOffset, littleEndian) /
31619                     dataView.getInt32(dataOffset + 4, littleEndian);
31620             },
31621             size: 8
31622         }
31623     },
31624     
31625     footer : {
31626         STANDARD : [
31627             {
31628                 tag : 'div',
31629                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31630                 action : 'rotate-left',
31631                 cn : [
31632                     {
31633                         tag : 'button',
31634                         cls : 'btn btn-default',
31635                         html : '<i class="fa fa-undo"></i>'
31636                     }
31637                 ]
31638             },
31639             {
31640                 tag : 'div',
31641                 cls : 'btn-group roo-upload-cropbox-picture',
31642                 action : 'picture',
31643                 cn : [
31644                     {
31645                         tag : 'button',
31646                         cls : 'btn btn-default',
31647                         html : '<i class="fa fa-picture-o"></i>'
31648                     }
31649                 ]
31650             },
31651             {
31652                 tag : 'div',
31653                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31654                 action : 'rotate-right',
31655                 cn : [
31656                     {
31657                         tag : 'button',
31658                         cls : 'btn btn-default',
31659                         html : '<i class="fa fa-repeat"></i>'
31660                     }
31661                 ]
31662             }
31663         ],
31664         DOCUMENT : [
31665             {
31666                 tag : 'div',
31667                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31668                 action : 'rotate-left',
31669                 cn : [
31670                     {
31671                         tag : 'button',
31672                         cls : 'btn btn-default',
31673                         html : '<i class="fa fa-undo"></i>'
31674                     }
31675                 ]
31676             },
31677             {
31678                 tag : 'div',
31679                 cls : 'btn-group roo-upload-cropbox-download',
31680                 action : 'download',
31681                 cn : [
31682                     {
31683                         tag : 'button',
31684                         cls : 'btn btn-default',
31685                         html : '<i class="fa fa-download"></i>'
31686                     }
31687                 ]
31688             },
31689             {
31690                 tag : 'div',
31691                 cls : 'btn-group roo-upload-cropbox-crop',
31692                 action : 'crop',
31693                 cn : [
31694                     {
31695                         tag : 'button',
31696                         cls : 'btn btn-default',
31697                         html : '<i class="fa fa-crop"></i>'
31698                     }
31699                 ]
31700             },
31701             {
31702                 tag : 'div',
31703                 cls : 'btn-group roo-upload-cropbox-trash',
31704                 action : 'trash',
31705                 cn : [
31706                     {
31707                         tag : 'button',
31708                         cls : 'btn btn-default',
31709                         html : '<i class="fa fa-trash"></i>'
31710                     }
31711                 ]
31712             },
31713             {
31714                 tag : 'div',
31715                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31716                 action : 'rotate-right',
31717                 cn : [
31718                     {
31719                         tag : 'button',
31720                         cls : 'btn btn-default',
31721                         html : '<i class="fa fa-repeat"></i>'
31722                     }
31723                 ]
31724             }
31725         ],
31726         ROTATOR : [
31727             {
31728                 tag : 'div',
31729                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31730                 action : 'rotate-left',
31731                 cn : [
31732                     {
31733                         tag : 'button',
31734                         cls : 'btn btn-default',
31735                         html : '<i class="fa fa-undo"></i>'
31736                     }
31737                 ]
31738             },
31739             {
31740                 tag : 'div',
31741                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31742                 action : 'rotate-right',
31743                 cn : [
31744                     {
31745                         tag : 'button',
31746                         cls : 'btn btn-default',
31747                         html : '<i class="fa fa-repeat"></i>'
31748                     }
31749                 ]
31750             }
31751         ]
31752     }
31753 });
31754
31755 /*
31756 * Licence: LGPL
31757 */
31758
31759 /**
31760  * @class Roo.bootstrap.DocumentManager
31761  * @extends Roo.bootstrap.Component
31762  * Bootstrap DocumentManager class
31763  * @cfg {String} paramName default 'imageUpload'
31764  * @cfg {String} toolTipName default 'filename'
31765  * @cfg {String} method default POST
31766  * @cfg {String} url action url
31767  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31768  * @cfg {Boolean} multiple multiple upload default true
31769  * @cfg {Number} thumbSize default 300
31770  * @cfg {String} fieldLabel
31771  * @cfg {Number} labelWidth default 4
31772  * @cfg {String} labelAlign (left|top) default left
31773  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31774 * @cfg {Number} labellg set the width of label (1-12)
31775  * @cfg {Number} labelmd set the width of label (1-12)
31776  * @cfg {Number} labelsm set the width of label (1-12)
31777  * @cfg {Number} labelxs set the width of label (1-12)
31778  * 
31779  * @constructor
31780  * Create a new DocumentManager
31781  * @param {Object} config The config object
31782  */
31783
31784 Roo.bootstrap.DocumentManager = function(config){
31785     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31786     
31787     this.files = [];
31788     this.delegates = [];
31789     
31790     this.addEvents({
31791         /**
31792          * @event initial
31793          * Fire when initial the DocumentManager
31794          * @param {Roo.bootstrap.DocumentManager} this
31795          */
31796         "initial" : true,
31797         /**
31798          * @event inspect
31799          * inspect selected file
31800          * @param {Roo.bootstrap.DocumentManager} this
31801          * @param {File} file
31802          */
31803         "inspect" : true,
31804         /**
31805          * @event exception
31806          * Fire when xhr load exception
31807          * @param {Roo.bootstrap.DocumentManager} this
31808          * @param {XMLHttpRequest} xhr
31809          */
31810         "exception" : true,
31811         /**
31812          * @event afterupload
31813          * Fire when xhr load exception
31814          * @param {Roo.bootstrap.DocumentManager} this
31815          * @param {XMLHttpRequest} xhr
31816          */
31817         "afterupload" : true,
31818         /**
31819          * @event prepare
31820          * prepare the form data
31821          * @param {Roo.bootstrap.DocumentManager} this
31822          * @param {Object} formData
31823          */
31824         "prepare" : true,
31825         /**
31826          * @event remove
31827          * Fire when remove the file
31828          * @param {Roo.bootstrap.DocumentManager} this
31829          * @param {Object} file
31830          */
31831         "remove" : true,
31832         /**
31833          * @event refresh
31834          * Fire after refresh the file
31835          * @param {Roo.bootstrap.DocumentManager} this
31836          */
31837         "refresh" : true,
31838         /**
31839          * @event click
31840          * Fire after click the image
31841          * @param {Roo.bootstrap.DocumentManager} this
31842          * @param {Object} file
31843          */
31844         "click" : true,
31845         /**
31846          * @event edit
31847          * Fire when upload a image and editable set to true
31848          * @param {Roo.bootstrap.DocumentManager} this
31849          * @param {Object} file
31850          */
31851         "edit" : true,
31852         /**
31853          * @event beforeselectfile
31854          * Fire before select file
31855          * @param {Roo.bootstrap.DocumentManager} this
31856          */
31857         "beforeselectfile" : true,
31858         /**
31859          * @event process
31860          * Fire before process file
31861          * @param {Roo.bootstrap.DocumentManager} this
31862          * @param {Object} file
31863          */
31864         "process" : true,
31865         /**
31866          * @event previewrendered
31867          * Fire when preview rendered
31868          * @param {Roo.bootstrap.DocumentManager} this
31869          * @param {Object} file
31870          */
31871         "previewrendered" : true,
31872         /**
31873          */
31874         "previewResize" : true
31875         
31876     });
31877 };
31878
31879 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31880     
31881     boxes : 0,
31882     inputName : '',
31883     thumbSize : 300,
31884     multiple : true,
31885     files : false,
31886     method : 'POST',
31887     url : '',
31888     paramName : 'imageUpload',
31889     toolTipName : 'filename',
31890     fieldLabel : '',
31891     labelWidth : 4,
31892     labelAlign : 'left',
31893     editable : true,
31894     delegates : false,
31895     xhr : false, 
31896     
31897     labellg : 0,
31898     labelmd : 0,
31899     labelsm : 0,
31900     labelxs : 0,
31901     
31902     getAutoCreate : function()
31903     {   
31904         var managerWidget = {
31905             tag : 'div',
31906             cls : 'roo-document-manager',
31907             cn : [
31908                 {
31909                     tag : 'input',
31910                     cls : 'roo-document-manager-selector',
31911                     type : 'file'
31912                 },
31913                 {
31914                     tag : 'div',
31915                     cls : 'roo-document-manager-uploader',
31916                     cn : [
31917                         {
31918                             tag : 'div',
31919                             cls : 'roo-document-manager-upload-btn',
31920                             html : '<i class="fa fa-plus"></i>'
31921                         }
31922                     ]
31923                     
31924                 }
31925             ]
31926         };
31927         
31928         var content = [
31929             {
31930                 tag : 'div',
31931                 cls : 'column col-md-12',
31932                 cn : managerWidget
31933             }
31934         ];
31935         
31936         if(this.fieldLabel.length){
31937             
31938             content = [
31939                 {
31940                     tag : 'div',
31941                     cls : 'column col-md-12',
31942                     html : this.fieldLabel
31943                 },
31944                 {
31945                     tag : 'div',
31946                     cls : 'column col-md-12',
31947                     cn : managerWidget
31948                 }
31949             ];
31950
31951             if(this.labelAlign == 'left'){
31952                 content = [
31953                     {
31954                         tag : 'div',
31955                         cls : 'column',
31956                         html : this.fieldLabel
31957                     },
31958                     {
31959                         tag : 'div',
31960                         cls : 'column',
31961                         cn : managerWidget
31962                     }
31963                 ];
31964                 
31965                 if(this.labelWidth > 12){
31966                     content[0].style = "width: " + this.labelWidth + 'px';
31967                 }
31968
31969                 if(this.labelWidth < 13 && this.labelmd == 0){
31970                     this.labelmd = this.labelWidth;
31971                 }
31972
31973                 if(this.labellg > 0){
31974                     content[0].cls += ' col-lg-' + this.labellg;
31975                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31976                 }
31977
31978                 if(this.labelmd > 0){
31979                     content[0].cls += ' col-md-' + this.labelmd;
31980                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31981                 }
31982
31983                 if(this.labelsm > 0){
31984                     content[0].cls += ' col-sm-' + this.labelsm;
31985                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31986                 }
31987
31988                 if(this.labelxs > 0){
31989                     content[0].cls += ' col-xs-' + this.labelxs;
31990                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31991                 }
31992                 
31993             }
31994         }
31995         
31996         var cfg = {
31997             tag : 'div',
31998             cls : 'row clearfix',
31999             cn : content
32000         };
32001         
32002         return cfg;
32003         
32004     },
32005     
32006     initEvents : function()
32007     {
32008         this.managerEl = this.el.select('.roo-document-manager', true).first();
32009         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32010         
32011         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32012         this.selectorEl.hide();
32013         
32014         if(this.multiple){
32015             this.selectorEl.attr('multiple', 'multiple');
32016         }
32017         
32018         this.selectorEl.on('change', this.onFileSelected, this);
32019         
32020         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32021         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32022         
32023         this.uploader.on('click', this.onUploaderClick, this);
32024         
32025         this.renderProgressDialog();
32026         
32027         var _this = this;
32028         
32029         window.addEventListener("resize", function() { _this.refresh(); } );
32030         
32031         this.fireEvent('initial', this);
32032     },
32033     
32034     renderProgressDialog : function()
32035     {
32036         var _this = this;
32037         
32038         this.progressDialog = new Roo.bootstrap.Modal({
32039             cls : 'roo-document-manager-progress-dialog',
32040             allow_close : false,
32041             animate : false,
32042             title : '',
32043             buttons : [
32044                 {
32045                     name  :'cancel',
32046                     weight : 'danger',
32047                     html : 'Cancel'
32048                 }
32049             ], 
32050             listeners : { 
32051                 btnclick : function() {
32052                     _this.uploadCancel();
32053                     this.hide();
32054                 }
32055             }
32056         });
32057          
32058         this.progressDialog.render(Roo.get(document.body));
32059          
32060         this.progress = new Roo.bootstrap.Progress({
32061             cls : 'roo-document-manager-progress',
32062             active : true,
32063             striped : true
32064         });
32065         
32066         this.progress.render(this.progressDialog.getChildContainer());
32067         
32068         this.progressBar = new Roo.bootstrap.ProgressBar({
32069             cls : 'roo-document-manager-progress-bar',
32070             aria_valuenow : 0,
32071             aria_valuemin : 0,
32072             aria_valuemax : 12,
32073             panel : 'success'
32074         });
32075         
32076         this.progressBar.render(this.progress.getChildContainer());
32077     },
32078     
32079     onUploaderClick : function(e)
32080     {
32081         e.preventDefault();
32082      
32083         if(this.fireEvent('beforeselectfile', this) != false){
32084             this.selectorEl.dom.click();
32085         }
32086         
32087     },
32088     
32089     onFileSelected : function(e)
32090     {
32091         e.preventDefault();
32092         
32093         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32094             return;
32095         }
32096         
32097         Roo.each(this.selectorEl.dom.files, function(file){
32098             if(this.fireEvent('inspect', this, file) != false){
32099                 this.files.push(file);
32100             }
32101         }, this);
32102         
32103         this.queue();
32104         
32105     },
32106     
32107     queue : function()
32108     {
32109         this.selectorEl.dom.value = '';
32110         
32111         if(!this.files || !this.files.length){
32112             return;
32113         }
32114         
32115         if(this.boxes > 0 && this.files.length > this.boxes){
32116             this.files = this.files.slice(0, this.boxes);
32117         }
32118         
32119         this.uploader.show();
32120         
32121         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32122             this.uploader.hide();
32123         }
32124         
32125         var _this = this;
32126         
32127         var files = [];
32128         
32129         var docs = [];
32130         
32131         Roo.each(this.files, function(file){
32132             
32133             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32134                 var f = this.renderPreview(file);
32135                 files.push(f);
32136                 return;
32137             }
32138             
32139             if(file.type.indexOf('image') != -1){
32140                 this.delegates.push(
32141                     (function(){
32142                         _this.process(file);
32143                     }).createDelegate(this)
32144                 );
32145         
32146                 return;
32147             }
32148             
32149             docs.push(
32150                 (function(){
32151                     _this.process(file);
32152                 }).createDelegate(this)
32153             );
32154             
32155         }, this);
32156         
32157         this.files = files;
32158         
32159         this.delegates = this.delegates.concat(docs);
32160         
32161         if(!this.delegates.length){
32162             this.refresh();
32163             return;
32164         }
32165         
32166         this.progressBar.aria_valuemax = this.delegates.length;
32167         
32168         this.arrange();
32169         
32170         return;
32171     },
32172     
32173     arrange : function()
32174     {
32175         if(!this.delegates.length){
32176             this.progressDialog.hide();
32177             this.refresh();
32178             return;
32179         }
32180         
32181         var delegate = this.delegates.shift();
32182         
32183         this.progressDialog.show();
32184         
32185         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32186         
32187         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32188         
32189         delegate();
32190     },
32191     
32192     refresh : function()
32193     {
32194         this.uploader.show();
32195         
32196         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32197             this.uploader.hide();
32198         }
32199         
32200         Roo.isTouch ? this.closable(false) : this.closable(true);
32201         
32202         this.fireEvent('refresh', this);
32203     },
32204     
32205     onRemove : function(e, el, o)
32206     {
32207         e.preventDefault();
32208         
32209         this.fireEvent('remove', this, o);
32210         
32211     },
32212     
32213     remove : function(o)
32214     {
32215         var files = [];
32216         
32217         Roo.each(this.files, function(file){
32218             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32219                 files.push(file);
32220                 return;
32221             }
32222
32223             o.target.remove();
32224
32225         }, this);
32226         
32227         this.files = files;
32228         
32229         this.refresh();
32230     },
32231     
32232     clear : function()
32233     {
32234         Roo.each(this.files, function(file){
32235             if(!file.target){
32236                 return;
32237             }
32238             
32239             file.target.remove();
32240
32241         }, this);
32242         
32243         this.files = [];
32244         
32245         this.refresh();
32246     },
32247     
32248     onClick : function(e, el, o)
32249     {
32250         e.preventDefault();
32251         
32252         this.fireEvent('click', this, o);
32253         
32254     },
32255     
32256     closable : function(closable)
32257     {
32258         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32259             
32260             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32261             
32262             if(closable){
32263                 el.show();
32264                 return;
32265             }
32266             
32267             el.hide();
32268             
32269         }, this);
32270     },
32271     
32272     xhrOnLoad : function(xhr)
32273     {
32274         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32275             el.remove();
32276         }, this);
32277         
32278         if (xhr.readyState !== 4) {
32279             this.arrange();
32280             this.fireEvent('exception', this, xhr);
32281             return;
32282         }
32283
32284         var response = Roo.decode(xhr.responseText);
32285         
32286         if(!response.success){
32287             this.arrange();
32288             this.fireEvent('exception', this, xhr);
32289             return;
32290         }
32291         
32292         var file = this.renderPreview(response.data);
32293         
32294         this.files.push(file);
32295         
32296         this.arrange();
32297         
32298         this.fireEvent('afterupload', this, xhr);
32299         
32300     },
32301     
32302     xhrOnError : function(xhr)
32303     {
32304         Roo.log('xhr on error');
32305         
32306         var response = Roo.decode(xhr.responseText);
32307           
32308         Roo.log(response);
32309         
32310         this.arrange();
32311     },
32312     
32313     process : function(file)
32314     {
32315         if(this.fireEvent('process', this, file) !== false){
32316             if(this.editable && file.type.indexOf('image') != -1){
32317                 this.fireEvent('edit', this, file);
32318                 return;
32319             }
32320
32321             this.uploadStart(file, false);
32322
32323             return;
32324         }
32325         
32326     },
32327     
32328     uploadStart : function(file, crop)
32329     {
32330         this.xhr = new XMLHttpRequest();
32331         
32332         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32333             this.arrange();
32334             return;
32335         }
32336         
32337         file.xhr = this.xhr;
32338             
32339         this.managerEl.createChild({
32340             tag : 'div',
32341             cls : 'roo-document-manager-loading',
32342             cn : [
32343                 {
32344                     tag : 'div',
32345                     tooltip : file.name,
32346                     cls : 'roo-document-manager-thumb',
32347                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32348                 }
32349             ]
32350
32351         });
32352
32353         this.xhr.open(this.method, this.url, true);
32354         
32355         var headers = {
32356             "Accept": "application/json",
32357             "Cache-Control": "no-cache",
32358             "X-Requested-With": "XMLHttpRequest"
32359         };
32360         
32361         for (var headerName in headers) {
32362             var headerValue = headers[headerName];
32363             if (headerValue) {
32364                 this.xhr.setRequestHeader(headerName, headerValue);
32365             }
32366         }
32367         
32368         var _this = this;
32369         
32370         this.xhr.onload = function()
32371         {
32372             _this.xhrOnLoad(_this.xhr);
32373         }
32374         
32375         this.xhr.onerror = function()
32376         {
32377             _this.xhrOnError(_this.xhr);
32378         }
32379         
32380         var formData = new FormData();
32381
32382         formData.append('returnHTML', 'NO');
32383         
32384         if(crop){
32385             formData.append('crop', crop);
32386         }
32387         
32388         formData.append(this.paramName, file, file.name);
32389         
32390         var options = {
32391             file : file, 
32392             manually : false
32393         };
32394         
32395         if(this.fireEvent('prepare', this, formData, options) != false){
32396             
32397             if(options.manually){
32398                 return;
32399             }
32400             
32401             this.xhr.send(formData);
32402             return;
32403         };
32404         
32405         this.uploadCancel();
32406     },
32407     
32408     uploadCancel : function()
32409     {
32410         if (this.xhr) {
32411             this.xhr.abort();
32412         }
32413         
32414         this.delegates = [];
32415         
32416         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32417             el.remove();
32418         }, this);
32419         
32420         this.arrange();
32421     },
32422     
32423     renderPreview : function(file)
32424     {
32425         if(typeof(file.target) != 'undefined' && file.target){
32426             return file;
32427         }
32428         
32429         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32430         
32431         var previewEl = this.managerEl.createChild({
32432             tag : 'div',
32433             cls : 'roo-document-manager-preview',
32434             cn : [
32435                 {
32436                     tag : 'div',
32437                     tooltip : file[this.toolTipName],
32438                     cls : 'roo-document-manager-thumb',
32439                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32440                 },
32441                 {
32442                     tag : 'button',
32443                     cls : 'close',
32444                     html : '<i class="fa fa-times-circle"></i>'
32445                 }
32446             ]
32447         });
32448
32449         var close = previewEl.select('button.close', true).first();
32450
32451         close.on('click', this.onRemove, this, file);
32452
32453         file.target = previewEl;
32454
32455         var image = previewEl.select('img', true).first();
32456         
32457         var _this = this;
32458         
32459         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32460         
32461         image.on('click', this.onClick, this, file);
32462         
32463         this.fireEvent('previewrendered', this, file);
32464         
32465         return file;
32466         
32467     },
32468     
32469     onPreviewLoad : function(file, image)
32470     {
32471         if(typeof(file.target) == 'undefined' || !file.target){
32472             return;
32473         }
32474         
32475         var width = image.dom.naturalWidth || image.dom.width;
32476         var height = image.dom.naturalHeight || image.dom.height;
32477         
32478         if(!this.previewResize) {
32479             return;
32480         }
32481         
32482         if(width > height){
32483             file.target.addClass('wide');
32484             return;
32485         }
32486         
32487         file.target.addClass('tall');
32488         return;
32489         
32490     },
32491     
32492     uploadFromSource : function(file, crop)
32493     {
32494         this.xhr = new XMLHttpRequest();
32495         
32496         this.managerEl.createChild({
32497             tag : 'div',
32498             cls : 'roo-document-manager-loading',
32499             cn : [
32500                 {
32501                     tag : 'div',
32502                     tooltip : file.name,
32503                     cls : 'roo-document-manager-thumb',
32504                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32505                 }
32506             ]
32507
32508         });
32509
32510         this.xhr.open(this.method, this.url, true);
32511         
32512         var headers = {
32513             "Accept": "application/json",
32514             "Cache-Control": "no-cache",
32515             "X-Requested-With": "XMLHttpRequest"
32516         };
32517         
32518         for (var headerName in headers) {
32519             var headerValue = headers[headerName];
32520             if (headerValue) {
32521                 this.xhr.setRequestHeader(headerName, headerValue);
32522             }
32523         }
32524         
32525         var _this = this;
32526         
32527         this.xhr.onload = function()
32528         {
32529             _this.xhrOnLoad(_this.xhr);
32530         }
32531         
32532         this.xhr.onerror = function()
32533         {
32534             _this.xhrOnError(_this.xhr);
32535         }
32536         
32537         var formData = new FormData();
32538
32539         formData.append('returnHTML', 'NO');
32540         
32541         formData.append('crop', crop);
32542         
32543         if(typeof(file.filename) != 'undefined'){
32544             formData.append('filename', file.filename);
32545         }
32546         
32547         if(typeof(file.mimetype) != 'undefined'){
32548             formData.append('mimetype', file.mimetype);
32549         }
32550         
32551         Roo.log(formData);
32552         
32553         if(this.fireEvent('prepare', this, formData) != false){
32554             this.xhr.send(formData);
32555         };
32556     }
32557 });
32558
32559 /*
32560 * Licence: LGPL
32561 */
32562
32563 /**
32564  * @class Roo.bootstrap.DocumentViewer
32565  * @extends Roo.bootstrap.Component
32566  * Bootstrap DocumentViewer class
32567  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32568  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32569  * 
32570  * @constructor
32571  * Create a new DocumentViewer
32572  * @param {Object} config The config object
32573  */
32574
32575 Roo.bootstrap.DocumentViewer = function(config){
32576     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32577     
32578     this.addEvents({
32579         /**
32580          * @event initial
32581          * Fire after initEvent
32582          * @param {Roo.bootstrap.DocumentViewer} this
32583          */
32584         "initial" : true,
32585         /**
32586          * @event click
32587          * Fire after click
32588          * @param {Roo.bootstrap.DocumentViewer} this
32589          */
32590         "click" : true,
32591         /**
32592          * @event download
32593          * Fire after download button
32594          * @param {Roo.bootstrap.DocumentViewer} this
32595          */
32596         "download" : true,
32597         /**
32598          * @event trash
32599          * Fire after trash button
32600          * @param {Roo.bootstrap.DocumentViewer} this
32601          */
32602         "trash" : true
32603         
32604     });
32605 };
32606
32607 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32608     
32609     showDownload : true,
32610     
32611     showTrash : true,
32612     
32613     getAutoCreate : function()
32614     {
32615         var cfg = {
32616             tag : 'div',
32617             cls : 'roo-document-viewer',
32618             cn : [
32619                 {
32620                     tag : 'div',
32621                     cls : 'roo-document-viewer-body',
32622                     cn : [
32623                         {
32624                             tag : 'div',
32625                             cls : 'roo-document-viewer-thumb',
32626                             cn : [
32627                                 {
32628                                     tag : 'img',
32629                                     cls : 'roo-document-viewer-image'
32630                                 }
32631                             ]
32632                         }
32633                     ]
32634                 },
32635                 {
32636                     tag : 'div',
32637                     cls : 'roo-document-viewer-footer',
32638                     cn : {
32639                         tag : 'div',
32640                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32641                         cn : [
32642                             {
32643                                 tag : 'div',
32644                                 cls : 'btn-group roo-document-viewer-download',
32645                                 cn : [
32646                                     {
32647                                         tag : 'button',
32648                                         cls : 'btn btn-default',
32649                                         html : '<i class="fa fa-download"></i>'
32650                                     }
32651                                 ]
32652                             },
32653                             {
32654                                 tag : 'div',
32655                                 cls : 'btn-group roo-document-viewer-trash',
32656                                 cn : [
32657                                     {
32658                                         tag : 'button',
32659                                         cls : 'btn btn-default',
32660                                         html : '<i class="fa fa-trash"></i>'
32661                                     }
32662                                 ]
32663                             }
32664                         ]
32665                     }
32666                 }
32667             ]
32668         };
32669         
32670         return cfg;
32671     },
32672     
32673     initEvents : function()
32674     {
32675         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32676         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32677         
32678         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32679         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32680         
32681         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32682         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32683         
32684         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32685         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32686         
32687         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32688         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32689         
32690         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32691         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32692         
32693         this.bodyEl.on('click', this.onClick, this);
32694         this.downloadBtn.on('click', this.onDownload, this);
32695         this.trashBtn.on('click', this.onTrash, this);
32696         
32697         this.downloadBtn.hide();
32698         this.trashBtn.hide();
32699         
32700         if(this.showDownload){
32701             this.downloadBtn.show();
32702         }
32703         
32704         if(this.showTrash){
32705             this.trashBtn.show();
32706         }
32707         
32708         if(!this.showDownload && !this.showTrash) {
32709             this.footerEl.hide();
32710         }
32711         
32712     },
32713     
32714     initial : function()
32715     {
32716         this.fireEvent('initial', this);
32717         
32718     },
32719     
32720     onClick : function(e)
32721     {
32722         e.preventDefault();
32723         
32724         this.fireEvent('click', this);
32725     },
32726     
32727     onDownload : function(e)
32728     {
32729         e.preventDefault();
32730         
32731         this.fireEvent('download', this);
32732     },
32733     
32734     onTrash : function(e)
32735     {
32736         e.preventDefault();
32737         
32738         this.fireEvent('trash', this);
32739     }
32740     
32741 });
32742 /*
32743  * - LGPL
32744  *
32745  * nav progress bar
32746  * 
32747  */
32748
32749 /**
32750  * @class Roo.bootstrap.NavProgressBar
32751  * @extends Roo.bootstrap.Component
32752  * Bootstrap NavProgressBar class
32753  * 
32754  * @constructor
32755  * Create a new nav progress bar
32756  * @param {Object} config The config object
32757  */
32758
32759 Roo.bootstrap.NavProgressBar = function(config){
32760     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32761
32762     this.bullets = this.bullets || [];
32763    
32764 //    Roo.bootstrap.NavProgressBar.register(this);
32765      this.addEvents({
32766         /**
32767              * @event changed
32768              * Fires when the active item changes
32769              * @param {Roo.bootstrap.NavProgressBar} this
32770              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32771              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32772          */
32773         'changed': true
32774      });
32775     
32776 };
32777
32778 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32779     
32780     bullets : [],
32781     barItems : [],
32782     
32783     getAutoCreate : function()
32784     {
32785         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32786         
32787         cfg = {
32788             tag : 'div',
32789             cls : 'roo-navigation-bar-group',
32790             cn : [
32791                 {
32792                     tag : 'div',
32793                     cls : 'roo-navigation-top-bar'
32794                 },
32795                 {
32796                     tag : 'div',
32797                     cls : 'roo-navigation-bullets-bar',
32798                     cn : [
32799                         {
32800                             tag : 'ul',
32801                             cls : 'roo-navigation-bar'
32802                         }
32803                     ]
32804                 },
32805                 
32806                 {
32807                     tag : 'div',
32808                     cls : 'roo-navigation-bottom-bar'
32809                 }
32810             ]
32811             
32812         };
32813         
32814         return cfg;
32815         
32816     },
32817     
32818     initEvents: function() 
32819     {
32820         
32821     },
32822     
32823     onRender : function(ct, position) 
32824     {
32825         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32826         
32827         if(this.bullets.length){
32828             Roo.each(this.bullets, function(b){
32829                this.addItem(b);
32830             }, this);
32831         }
32832         
32833         this.format();
32834         
32835     },
32836     
32837     addItem : function(cfg)
32838     {
32839         var item = new Roo.bootstrap.NavProgressItem(cfg);
32840         
32841         item.parentId = this.id;
32842         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32843         
32844         if(cfg.html){
32845             var top = new Roo.bootstrap.Element({
32846                 tag : 'div',
32847                 cls : 'roo-navigation-bar-text'
32848             });
32849             
32850             var bottom = new Roo.bootstrap.Element({
32851                 tag : 'div',
32852                 cls : 'roo-navigation-bar-text'
32853             });
32854             
32855             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32856             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32857             
32858             var topText = new Roo.bootstrap.Element({
32859                 tag : 'span',
32860                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32861             });
32862             
32863             var bottomText = new Roo.bootstrap.Element({
32864                 tag : 'span',
32865                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32866             });
32867             
32868             topText.onRender(top.el, null);
32869             bottomText.onRender(bottom.el, null);
32870             
32871             item.topEl = top;
32872             item.bottomEl = bottom;
32873         }
32874         
32875         this.barItems.push(item);
32876         
32877         return item;
32878     },
32879     
32880     getActive : function()
32881     {
32882         var active = false;
32883         
32884         Roo.each(this.barItems, function(v){
32885             
32886             if (!v.isActive()) {
32887                 return;
32888             }
32889             
32890             active = v;
32891             return false;
32892             
32893         });
32894         
32895         return active;
32896     },
32897     
32898     setActiveItem : function(item)
32899     {
32900         var prev = false;
32901         
32902         Roo.each(this.barItems, function(v){
32903             if (v.rid == item.rid) {
32904                 return ;
32905             }
32906             
32907             if (v.isActive()) {
32908                 v.setActive(false);
32909                 prev = v;
32910             }
32911         });
32912
32913         item.setActive(true);
32914         
32915         this.fireEvent('changed', this, item, prev);
32916     },
32917     
32918     getBarItem: function(rid)
32919     {
32920         var ret = false;
32921         
32922         Roo.each(this.barItems, function(e) {
32923             if (e.rid != rid) {
32924                 return;
32925             }
32926             
32927             ret =  e;
32928             return false;
32929         });
32930         
32931         return ret;
32932     },
32933     
32934     indexOfItem : function(item)
32935     {
32936         var index = false;
32937         
32938         Roo.each(this.barItems, function(v, i){
32939             
32940             if (v.rid != item.rid) {
32941                 return;
32942             }
32943             
32944             index = i;
32945             return false
32946         });
32947         
32948         return index;
32949     },
32950     
32951     setActiveNext : function()
32952     {
32953         var i = this.indexOfItem(this.getActive());
32954         
32955         if (i > this.barItems.length) {
32956             return;
32957         }
32958         
32959         this.setActiveItem(this.barItems[i+1]);
32960     },
32961     
32962     setActivePrev : function()
32963     {
32964         var i = this.indexOfItem(this.getActive());
32965         
32966         if (i  < 1) {
32967             return;
32968         }
32969         
32970         this.setActiveItem(this.barItems[i-1]);
32971     },
32972     
32973     format : function()
32974     {
32975         if(!this.barItems.length){
32976             return;
32977         }
32978      
32979         var width = 100 / this.barItems.length;
32980         
32981         Roo.each(this.barItems, function(i){
32982             i.el.setStyle('width', width + '%');
32983             i.topEl.el.setStyle('width', width + '%');
32984             i.bottomEl.el.setStyle('width', width + '%');
32985         }, this);
32986         
32987     }
32988     
32989 });
32990 /*
32991  * - LGPL
32992  *
32993  * Nav Progress Item
32994  * 
32995  */
32996
32997 /**
32998  * @class Roo.bootstrap.NavProgressItem
32999  * @extends Roo.bootstrap.Component
33000  * Bootstrap NavProgressItem class
33001  * @cfg {String} rid the reference id
33002  * @cfg {Boolean} active (true|false) Is item active default false
33003  * @cfg {Boolean} disabled (true|false) Is item active default false
33004  * @cfg {String} html
33005  * @cfg {String} position (top|bottom) text position default bottom
33006  * @cfg {String} icon show icon instead of number
33007  * 
33008  * @constructor
33009  * Create a new NavProgressItem
33010  * @param {Object} config The config object
33011  */
33012 Roo.bootstrap.NavProgressItem = function(config){
33013     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33014     this.addEvents({
33015         // raw events
33016         /**
33017          * @event click
33018          * The raw click event for the entire grid.
33019          * @param {Roo.bootstrap.NavProgressItem} this
33020          * @param {Roo.EventObject} e
33021          */
33022         "click" : true
33023     });
33024    
33025 };
33026
33027 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33028     
33029     rid : '',
33030     active : false,
33031     disabled : false,
33032     html : '',
33033     position : 'bottom',
33034     icon : false,
33035     
33036     getAutoCreate : function()
33037     {
33038         var iconCls = 'roo-navigation-bar-item-icon';
33039         
33040         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33041         
33042         var cfg = {
33043             tag: 'li',
33044             cls: 'roo-navigation-bar-item',
33045             cn : [
33046                 {
33047                     tag : 'i',
33048                     cls : iconCls
33049                 }
33050             ]
33051         };
33052         
33053         if(this.active){
33054             cfg.cls += ' active';
33055         }
33056         if(this.disabled){
33057             cfg.cls += ' disabled';
33058         }
33059         
33060         return cfg;
33061     },
33062     
33063     disable : function()
33064     {
33065         this.setDisabled(true);
33066     },
33067     
33068     enable : function()
33069     {
33070         this.setDisabled(false);
33071     },
33072     
33073     initEvents: function() 
33074     {
33075         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33076         
33077         this.iconEl.on('click', this.onClick, this);
33078     },
33079     
33080     onClick : function(e)
33081     {
33082         e.preventDefault();
33083         
33084         if(this.disabled){
33085             return;
33086         }
33087         
33088         if(this.fireEvent('click', this, e) === false){
33089             return;
33090         };
33091         
33092         this.parent().setActiveItem(this);
33093     },
33094     
33095     isActive: function () 
33096     {
33097         return this.active;
33098     },
33099     
33100     setActive : function(state)
33101     {
33102         if(this.active == state){
33103             return;
33104         }
33105         
33106         this.active = state;
33107         
33108         if (state) {
33109             this.el.addClass('active');
33110             return;
33111         }
33112         
33113         this.el.removeClass('active');
33114         
33115         return;
33116     },
33117     
33118     setDisabled : function(state)
33119     {
33120         if(this.disabled == state){
33121             return;
33122         }
33123         
33124         this.disabled = state;
33125         
33126         if (state) {
33127             this.el.addClass('disabled');
33128             return;
33129         }
33130         
33131         this.el.removeClass('disabled');
33132     },
33133     
33134     tooltipEl : function()
33135     {
33136         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33137     }
33138 });
33139  
33140
33141  /*
33142  * - LGPL
33143  *
33144  * FieldLabel
33145  * 
33146  */
33147
33148 /**
33149  * @class Roo.bootstrap.FieldLabel
33150  * @extends Roo.bootstrap.Component
33151  * Bootstrap FieldLabel class
33152  * @cfg {String} html contents of the element
33153  * @cfg {String} tag tag of the element default label
33154  * @cfg {String} cls class of the element
33155  * @cfg {String} target label target 
33156  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33157  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33158  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33159  * @cfg {String} iconTooltip default "This field is required"
33160  * @cfg {String} indicatorpos (left|right) default left
33161  * 
33162  * @constructor
33163  * Create a new FieldLabel
33164  * @param {Object} config The config object
33165  */
33166
33167 Roo.bootstrap.FieldLabel = function(config){
33168     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33169     
33170     this.addEvents({
33171             /**
33172              * @event invalid
33173              * Fires after the field has been marked as invalid.
33174              * @param {Roo.form.FieldLabel} this
33175              * @param {String} msg The validation message
33176              */
33177             invalid : true,
33178             /**
33179              * @event valid
33180              * Fires after the field has been validated with no errors.
33181              * @param {Roo.form.FieldLabel} this
33182              */
33183             valid : true
33184         });
33185 };
33186
33187 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33188     
33189     tag: 'label',
33190     cls: '',
33191     html: '',
33192     target: '',
33193     allowBlank : true,
33194     invalidClass : 'has-warning',
33195     validClass : 'has-success',
33196     iconTooltip : 'This field is required',
33197     indicatorpos : 'left',
33198     
33199     getAutoCreate : function(){
33200         
33201         var cls = "";
33202         if (!this.allowBlank) {
33203             cls  = "visible";
33204         }
33205         
33206         var cfg = {
33207             tag : this.tag,
33208             cls : 'roo-bootstrap-field-label ' + this.cls,
33209             for : this.target,
33210             cn : [
33211                 {
33212                     tag : 'i',
33213                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33214                     tooltip : this.iconTooltip
33215                 },
33216                 {
33217                     tag : 'span',
33218                     html : this.html
33219                 }
33220             ] 
33221         };
33222         
33223         if(this.indicatorpos == 'right'){
33224             var cfg = {
33225                 tag : this.tag,
33226                 cls : 'roo-bootstrap-field-label ' + this.cls,
33227                 for : this.target,
33228                 cn : [
33229                     {
33230                         tag : 'span',
33231                         html : this.html
33232                     },
33233                     {
33234                         tag : 'i',
33235                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33236                         tooltip : this.iconTooltip
33237                     }
33238                 ] 
33239             };
33240         }
33241         
33242         return cfg;
33243     },
33244     
33245     initEvents: function() 
33246     {
33247         Roo.bootstrap.Element.superclass.initEvents.call(this);
33248         
33249         this.indicator = this.indicatorEl();
33250         
33251         if(this.indicator){
33252             this.indicator.removeClass('visible');
33253             this.indicator.addClass('invisible');
33254         }
33255         
33256         Roo.bootstrap.FieldLabel.register(this);
33257     },
33258     
33259     indicatorEl : function()
33260     {
33261         var indicator = this.el.select('i.roo-required-indicator',true).first();
33262         
33263         if(!indicator){
33264             return false;
33265         }
33266         
33267         return indicator;
33268         
33269     },
33270     
33271     /**
33272      * Mark this field as valid
33273      */
33274     markValid : function()
33275     {
33276         if(this.indicator){
33277             this.indicator.removeClass('visible');
33278             this.indicator.addClass('invisible');
33279         }
33280         if (Roo.bootstrap.version == 3) {
33281             this.el.removeClass(this.invalidClass);
33282             this.el.addClass(this.validClass);
33283         } else {
33284             this.el.removeClass('is-invalid');
33285             this.el.addClass('is-valid');
33286         }
33287         
33288         
33289         this.fireEvent('valid', this);
33290     },
33291     
33292     /**
33293      * Mark this field as invalid
33294      * @param {String} msg The validation message
33295      */
33296     markInvalid : function(msg)
33297     {
33298         if(this.indicator){
33299             this.indicator.removeClass('invisible');
33300             this.indicator.addClass('visible');
33301         }
33302           if (Roo.bootstrap.version == 3) {
33303             this.el.removeClass(this.validClass);
33304             this.el.addClass(this.invalidClass);
33305         } else {
33306             this.el.removeClass('is-valid');
33307             this.el.addClass('is-invalid');
33308         }
33309         
33310         
33311         this.fireEvent('invalid', this, msg);
33312     }
33313     
33314    
33315 });
33316
33317 Roo.apply(Roo.bootstrap.FieldLabel, {
33318     
33319     groups: {},
33320     
33321      /**
33322     * register a FieldLabel Group
33323     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33324     */
33325     register : function(label)
33326     {
33327         if(this.groups.hasOwnProperty(label.target)){
33328             return;
33329         }
33330      
33331         this.groups[label.target] = label;
33332         
33333     },
33334     /**
33335     * fetch a FieldLabel Group based on the target
33336     * @param {string} target
33337     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33338     */
33339     get: function(target) {
33340         if (typeof(this.groups[target]) == 'undefined') {
33341             return false;
33342         }
33343         
33344         return this.groups[target] ;
33345     }
33346 });
33347
33348  
33349
33350  /*
33351  * - LGPL
33352  *
33353  * page DateSplitField.
33354  * 
33355  */
33356
33357
33358 /**
33359  * @class Roo.bootstrap.DateSplitField
33360  * @extends Roo.bootstrap.Component
33361  * Bootstrap DateSplitField class
33362  * @cfg {string} fieldLabel - the label associated
33363  * @cfg {Number} labelWidth set the width of label (0-12)
33364  * @cfg {String} labelAlign (top|left)
33365  * @cfg {Boolean} dayAllowBlank (true|false) default false
33366  * @cfg {Boolean} monthAllowBlank (true|false) default false
33367  * @cfg {Boolean} yearAllowBlank (true|false) default false
33368  * @cfg {string} dayPlaceholder 
33369  * @cfg {string} monthPlaceholder
33370  * @cfg {string} yearPlaceholder
33371  * @cfg {string} dayFormat default 'd'
33372  * @cfg {string} monthFormat default 'm'
33373  * @cfg {string} yearFormat default 'Y'
33374  * @cfg {Number} labellg set the width of label (1-12)
33375  * @cfg {Number} labelmd set the width of label (1-12)
33376  * @cfg {Number} labelsm set the width of label (1-12)
33377  * @cfg {Number} labelxs set the width of label (1-12)
33378
33379  *     
33380  * @constructor
33381  * Create a new DateSplitField
33382  * @param {Object} config The config object
33383  */
33384
33385 Roo.bootstrap.DateSplitField = function(config){
33386     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33387     
33388     this.addEvents({
33389         // raw events
33390          /**
33391          * @event years
33392          * getting the data of years
33393          * @param {Roo.bootstrap.DateSplitField} this
33394          * @param {Object} years
33395          */
33396         "years" : true,
33397         /**
33398          * @event days
33399          * getting the data of days
33400          * @param {Roo.bootstrap.DateSplitField} this
33401          * @param {Object} days
33402          */
33403         "days" : true,
33404         /**
33405          * @event invalid
33406          * Fires after the field has been marked as invalid.
33407          * @param {Roo.form.Field} this
33408          * @param {String} msg The validation message
33409          */
33410         invalid : true,
33411        /**
33412          * @event valid
33413          * Fires after the field has been validated with no errors.
33414          * @param {Roo.form.Field} this
33415          */
33416         valid : true
33417     });
33418 };
33419
33420 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33421     
33422     fieldLabel : '',
33423     labelAlign : 'top',
33424     labelWidth : 3,
33425     dayAllowBlank : false,
33426     monthAllowBlank : false,
33427     yearAllowBlank : false,
33428     dayPlaceholder : '',
33429     monthPlaceholder : '',
33430     yearPlaceholder : '',
33431     dayFormat : 'd',
33432     monthFormat : 'm',
33433     yearFormat : 'Y',
33434     isFormField : true,
33435     labellg : 0,
33436     labelmd : 0,
33437     labelsm : 0,
33438     labelxs : 0,
33439     
33440     getAutoCreate : function()
33441     {
33442         var cfg = {
33443             tag : 'div',
33444             cls : 'row roo-date-split-field-group',
33445             cn : [
33446                 {
33447                     tag : 'input',
33448                     type : 'hidden',
33449                     cls : 'form-hidden-field roo-date-split-field-group-value',
33450                     name : this.name
33451                 }
33452             ]
33453         };
33454         
33455         var labelCls = 'col-md-12';
33456         var contentCls = 'col-md-4';
33457         
33458         if(this.fieldLabel){
33459             
33460             var label = {
33461                 tag : 'div',
33462                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33463                 cn : [
33464                     {
33465                         tag : 'label',
33466                         html : this.fieldLabel
33467                     }
33468                 ]
33469             };
33470             
33471             if(this.labelAlign == 'left'){
33472             
33473                 if(this.labelWidth > 12){
33474                     label.style = "width: " + this.labelWidth + 'px';
33475                 }
33476
33477                 if(this.labelWidth < 13 && this.labelmd == 0){
33478                     this.labelmd = this.labelWidth;
33479                 }
33480
33481                 if(this.labellg > 0){
33482                     labelCls = ' col-lg-' + this.labellg;
33483                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33484                 }
33485
33486                 if(this.labelmd > 0){
33487                     labelCls = ' col-md-' + this.labelmd;
33488                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33489                 }
33490
33491                 if(this.labelsm > 0){
33492                     labelCls = ' col-sm-' + this.labelsm;
33493                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33494                 }
33495
33496                 if(this.labelxs > 0){
33497                     labelCls = ' col-xs-' + this.labelxs;
33498                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33499                 }
33500             }
33501             
33502             label.cls += ' ' + labelCls;
33503             
33504             cfg.cn.push(label);
33505         }
33506         
33507         Roo.each(['day', 'month', 'year'], function(t){
33508             cfg.cn.push({
33509                 tag : 'div',
33510                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33511             });
33512         }, this);
33513         
33514         return cfg;
33515     },
33516     
33517     inputEl: function ()
33518     {
33519         return this.el.select('.roo-date-split-field-group-value', true).first();
33520     },
33521     
33522     onRender : function(ct, position) 
33523     {
33524         var _this = this;
33525         
33526         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33527         
33528         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33529         
33530         this.dayField = new Roo.bootstrap.ComboBox({
33531             allowBlank : this.dayAllowBlank,
33532             alwaysQuery : true,
33533             displayField : 'value',
33534             editable : false,
33535             fieldLabel : '',
33536             forceSelection : true,
33537             mode : 'local',
33538             placeholder : this.dayPlaceholder,
33539             selectOnFocus : true,
33540             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33541             triggerAction : 'all',
33542             typeAhead : true,
33543             valueField : 'value',
33544             store : new Roo.data.SimpleStore({
33545                 data : (function() {    
33546                     var days = [];
33547                     _this.fireEvent('days', _this, days);
33548                     return days;
33549                 })(),
33550                 fields : [ 'value' ]
33551             }),
33552             listeners : {
33553                 select : function (_self, record, index)
33554                 {
33555                     _this.setValue(_this.getValue());
33556                 }
33557             }
33558         });
33559
33560         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33561         
33562         this.monthField = new Roo.bootstrap.MonthField({
33563             after : '<i class=\"fa fa-calendar\"></i>',
33564             allowBlank : this.monthAllowBlank,
33565             placeholder : this.monthPlaceholder,
33566             readOnly : true,
33567             listeners : {
33568                 render : function (_self)
33569                 {
33570                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33571                         e.preventDefault();
33572                         _self.focus();
33573                     });
33574                 },
33575                 select : function (_self, oldvalue, newvalue)
33576                 {
33577                     _this.setValue(_this.getValue());
33578                 }
33579             }
33580         });
33581         
33582         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33583         
33584         this.yearField = new Roo.bootstrap.ComboBox({
33585             allowBlank : this.yearAllowBlank,
33586             alwaysQuery : true,
33587             displayField : 'value',
33588             editable : false,
33589             fieldLabel : '',
33590             forceSelection : true,
33591             mode : 'local',
33592             placeholder : this.yearPlaceholder,
33593             selectOnFocus : true,
33594             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33595             triggerAction : 'all',
33596             typeAhead : true,
33597             valueField : 'value',
33598             store : new Roo.data.SimpleStore({
33599                 data : (function() {
33600                     var years = [];
33601                     _this.fireEvent('years', _this, years);
33602                     return years;
33603                 })(),
33604                 fields : [ 'value' ]
33605             }),
33606             listeners : {
33607                 select : function (_self, record, index)
33608                 {
33609                     _this.setValue(_this.getValue());
33610                 }
33611             }
33612         });
33613
33614         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33615     },
33616     
33617     setValue : function(v, format)
33618     {
33619         this.inputEl.dom.value = v;
33620         
33621         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33622         
33623         var d = Date.parseDate(v, f);
33624         
33625         if(!d){
33626             this.validate();
33627             return;
33628         }
33629         
33630         this.setDay(d.format(this.dayFormat));
33631         this.setMonth(d.format(this.monthFormat));
33632         this.setYear(d.format(this.yearFormat));
33633         
33634         this.validate();
33635         
33636         return;
33637     },
33638     
33639     setDay : function(v)
33640     {
33641         this.dayField.setValue(v);
33642         this.inputEl.dom.value = this.getValue();
33643         this.validate();
33644         return;
33645     },
33646     
33647     setMonth : function(v)
33648     {
33649         this.monthField.setValue(v, true);
33650         this.inputEl.dom.value = this.getValue();
33651         this.validate();
33652         return;
33653     },
33654     
33655     setYear : function(v)
33656     {
33657         this.yearField.setValue(v);
33658         this.inputEl.dom.value = this.getValue();
33659         this.validate();
33660         return;
33661     },
33662     
33663     getDay : function()
33664     {
33665         return this.dayField.getValue();
33666     },
33667     
33668     getMonth : function()
33669     {
33670         return this.monthField.getValue();
33671     },
33672     
33673     getYear : function()
33674     {
33675         return this.yearField.getValue();
33676     },
33677     
33678     getValue : function()
33679     {
33680         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33681         
33682         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33683         
33684         return date;
33685     },
33686     
33687     reset : function()
33688     {
33689         this.setDay('');
33690         this.setMonth('');
33691         this.setYear('');
33692         this.inputEl.dom.value = '';
33693         this.validate();
33694         return;
33695     },
33696     
33697     validate : function()
33698     {
33699         var d = this.dayField.validate();
33700         var m = this.monthField.validate();
33701         var y = this.yearField.validate();
33702         
33703         var valid = true;
33704         
33705         if(
33706                 (!this.dayAllowBlank && !d) ||
33707                 (!this.monthAllowBlank && !m) ||
33708                 (!this.yearAllowBlank && !y)
33709         ){
33710             valid = false;
33711         }
33712         
33713         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33714             return valid;
33715         }
33716         
33717         if(valid){
33718             this.markValid();
33719             return valid;
33720         }
33721         
33722         this.markInvalid();
33723         
33724         return valid;
33725     },
33726     
33727     markValid : function()
33728     {
33729         
33730         var label = this.el.select('label', true).first();
33731         var icon = this.el.select('i.fa-star', true).first();
33732
33733         if(label && icon){
33734             icon.remove();
33735         }
33736         
33737         this.fireEvent('valid', this);
33738     },
33739     
33740      /**
33741      * Mark this field as invalid
33742      * @param {String} msg The validation message
33743      */
33744     markInvalid : function(msg)
33745     {
33746         
33747         var label = this.el.select('label', true).first();
33748         var icon = this.el.select('i.fa-star', true).first();
33749
33750         if(label && !icon){
33751             this.el.select('.roo-date-split-field-label', true).createChild({
33752                 tag : 'i',
33753                 cls : 'text-danger fa fa-lg fa-star',
33754                 tooltip : 'This field is required',
33755                 style : 'margin-right:5px;'
33756             }, label, true);
33757         }
33758         
33759         this.fireEvent('invalid', this, msg);
33760     },
33761     
33762     clearInvalid : function()
33763     {
33764         var label = this.el.select('label', true).first();
33765         var icon = this.el.select('i.fa-star', true).first();
33766
33767         if(label && icon){
33768             icon.remove();
33769         }
33770         
33771         this.fireEvent('valid', this);
33772     },
33773     
33774     getName: function()
33775     {
33776         return this.name;
33777     }
33778     
33779 });
33780
33781  /**
33782  *
33783  * This is based on 
33784  * http://masonry.desandro.com
33785  *
33786  * The idea is to render all the bricks based on vertical width...
33787  *
33788  * The original code extends 'outlayer' - we might need to use that....
33789  * 
33790  */
33791
33792
33793 /**
33794  * @class Roo.bootstrap.LayoutMasonry
33795  * @extends Roo.bootstrap.Component
33796  * Bootstrap Layout Masonry class
33797  * 
33798  * @constructor
33799  * Create a new Element
33800  * @param {Object} config The config object
33801  */
33802
33803 Roo.bootstrap.LayoutMasonry = function(config){
33804     
33805     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33806     
33807     this.bricks = [];
33808     
33809     Roo.bootstrap.LayoutMasonry.register(this);
33810     
33811     this.addEvents({
33812         // raw events
33813         /**
33814          * @event layout
33815          * Fire after layout the items
33816          * @param {Roo.bootstrap.LayoutMasonry} this
33817          * @param {Roo.EventObject} e
33818          */
33819         "layout" : true
33820     });
33821     
33822 };
33823
33824 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33825     
33826     /**
33827      * @cfg {Boolean} isLayoutInstant = no animation?
33828      */   
33829     isLayoutInstant : false, // needed?
33830    
33831     /**
33832      * @cfg {Number} boxWidth  width of the columns
33833      */   
33834     boxWidth : 450,
33835     
33836       /**
33837      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33838      */   
33839     boxHeight : 0,
33840     
33841     /**
33842      * @cfg {Number} padWidth padding below box..
33843      */   
33844     padWidth : 10, 
33845     
33846     /**
33847      * @cfg {Number} gutter gutter width..
33848      */   
33849     gutter : 10,
33850     
33851      /**
33852      * @cfg {Number} maxCols maximum number of columns
33853      */   
33854     
33855     maxCols: 0,
33856     
33857     /**
33858      * @cfg {Boolean} isAutoInitial defalut true
33859      */   
33860     isAutoInitial : true, 
33861     
33862     containerWidth: 0,
33863     
33864     /**
33865      * @cfg {Boolean} isHorizontal defalut false
33866      */   
33867     isHorizontal : false, 
33868
33869     currentSize : null,
33870     
33871     tag: 'div',
33872     
33873     cls: '',
33874     
33875     bricks: null, //CompositeElement
33876     
33877     cols : 1,
33878     
33879     _isLayoutInited : false,
33880     
33881 //    isAlternative : false, // only use for vertical layout...
33882     
33883     /**
33884      * @cfg {Number} alternativePadWidth padding below box..
33885      */   
33886     alternativePadWidth : 50,
33887     
33888     selectedBrick : [],
33889     
33890     getAutoCreate : function(){
33891         
33892         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33893         
33894         var cfg = {
33895             tag: this.tag,
33896             cls: 'blog-masonary-wrapper ' + this.cls,
33897             cn : {
33898                 cls : 'mas-boxes masonary'
33899             }
33900         };
33901         
33902         return cfg;
33903     },
33904     
33905     getChildContainer: function( )
33906     {
33907         if (this.boxesEl) {
33908             return this.boxesEl;
33909         }
33910         
33911         this.boxesEl = this.el.select('.mas-boxes').first();
33912         
33913         return this.boxesEl;
33914     },
33915     
33916     
33917     initEvents : function()
33918     {
33919         var _this = this;
33920         
33921         if(this.isAutoInitial){
33922             Roo.log('hook children rendered');
33923             this.on('childrenrendered', function() {
33924                 Roo.log('children rendered');
33925                 _this.initial();
33926             } ,this);
33927         }
33928     },
33929     
33930     initial : function()
33931     {
33932         this.selectedBrick = [];
33933         
33934         this.currentSize = this.el.getBox(true);
33935         
33936         Roo.EventManager.onWindowResize(this.resize, this); 
33937
33938         if(!this.isAutoInitial){
33939             this.layout();
33940             return;
33941         }
33942         
33943         this.layout();
33944         
33945         return;
33946         //this.layout.defer(500,this);
33947         
33948     },
33949     
33950     resize : function()
33951     {
33952         var cs = this.el.getBox(true);
33953         
33954         if (
33955                 this.currentSize.width == cs.width && 
33956                 this.currentSize.x == cs.x && 
33957                 this.currentSize.height == cs.height && 
33958                 this.currentSize.y == cs.y 
33959         ) {
33960             Roo.log("no change in with or X or Y");
33961             return;
33962         }
33963         
33964         this.currentSize = cs;
33965         
33966         this.layout();
33967         
33968     },
33969     
33970     layout : function()
33971     {   
33972         this._resetLayout();
33973         
33974         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33975         
33976         this.layoutItems( isInstant );
33977       
33978         this._isLayoutInited = true;
33979         
33980         this.fireEvent('layout', this);
33981         
33982     },
33983     
33984     _resetLayout : function()
33985     {
33986         if(this.isHorizontal){
33987             this.horizontalMeasureColumns();
33988             return;
33989         }
33990         
33991         this.verticalMeasureColumns();
33992         
33993     },
33994     
33995     verticalMeasureColumns : function()
33996     {
33997         this.getContainerWidth();
33998         
33999 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34000 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34001 //            return;
34002 //        }
34003         
34004         var boxWidth = this.boxWidth + this.padWidth;
34005         
34006         if(this.containerWidth < this.boxWidth){
34007             boxWidth = this.containerWidth
34008         }
34009         
34010         var containerWidth = this.containerWidth;
34011         
34012         var cols = Math.floor(containerWidth / boxWidth);
34013         
34014         this.cols = Math.max( cols, 1 );
34015         
34016         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34017         
34018         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34019         
34020         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34021         
34022         this.colWidth = boxWidth + avail - this.padWidth;
34023         
34024         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34025         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34026     },
34027     
34028     horizontalMeasureColumns : function()
34029     {
34030         this.getContainerWidth();
34031         
34032         var boxWidth = this.boxWidth;
34033         
34034         if(this.containerWidth < boxWidth){
34035             boxWidth = this.containerWidth;
34036         }
34037         
34038         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34039         
34040         this.el.setHeight(boxWidth);
34041         
34042     },
34043     
34044     getContainerWidth : function()
34045     {
34046         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34047     },
34048     
34049     layoutItems : function( isInstant )
34050     {
34051         Roo.log(this.bricks);
34052         
34053         var items = Roo.apply([], this.bricks);
34054         
34055         if(this.isHorizontal){
34056             this._horizontalLayoutItems( items , isInstant );
34057             return;
34058         }
34059         
34060 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34061 //            this._verticalAlternativeLayoutItems( items , isInstant );
34062 //            return;
34063 //        }
34064         
34065         this._verticalLayoutItems( items , isInstant );
34066         
34067     },
34068     
34069     _verticalLayoutItems : function ( items , isInstant)
34070     {
34071         if ( !items || !items.length ) {
34072             return;
34073         }
34074         
34075         var standard = [
34076             ['xs', 'xs', 'xs', 'tall'],
34077             ['xs', 'xs', 'tall'],
34078             ['xs', 'xs', 'sm'],
34079             ['xs', 'xs', 'xs'],
34080             ['xs', 'tall'],
34081             ['xs', 'sm'],
34082             ['xs', 'xs'],
34083             ['xs'],
34084             
34085             ['sm', 'xs', 'xs'],
34086             ['sm', 'xs'],
34087             ['sm'],
34088             
34089             ['tall', 'xs', 'xs', 'xs'],
34090             ['tall', 'xs', 'xs'],
34091             ['tall', 'xs'],
34092             ['tall']
34093             
34094         ];
34095         
34096         var queue = [];
34097         
34098         var boxes = [];
34099         
34100         var box = [];
34101         
34102         Roo.each(items, function(item, k){
34103             
34104             switch (item.size) {
34105                 // these layouts take up a full box,
34106                 case 'md' :
34107                 case 'md-left' :
34108                 case 'md-right' :
34109                 case 'wide' :
34110                     
34111                     if(box.length){
34112                         boxes.push(box);
34113                         box = [];
34114                     }
34115                     
34116                     boxes.push([item]);
34117                     
34118                     break;
34119                     
34120                 case 'xs' :
34121                 case 'sm' :
34122                 case 'tall' :
34123                     
34124                     box.push(item);
34125                     
34126                     break;
34127                 default :
34128                     break;
34129                     
34130             }
34131             
34132         }, this);
34133         
34134         if(box.length){
34135             boxes.push(box);
34136             box = [];
34137         }
34138         
34139         var filterPattern = function(box, length)
34140         {
34141             if(!box.length){
34142                 return;
34143             }
34144             
34145             var match = false;
34146             
34147             var pattern = box.slice(0, length);
34148             
34149             var format = [];
34150             
34151             Roo.each(pattern, function(i){
34152                 format.push(i.size);
34153             }, this);
34154             
34155             Roo.each(standard, function(s){
34156                 
34157                 if(String(s) != String(format)){
34158                     return;
34159                 }
34160                 
34161                 match = true;
34162                 return false;
34163                 
34164             }, this);
34165             
34166             if(!match && length == 1){
34167                 return;
34168             }
34169             
34170             if(!match){
34171                 filterPattern(box, length - 1);
34172                 return;
34173             }
34174                 
34175             queue.push(pattern);
34176
34177             box = box.slice(length, box.length);
34178
34179             filterPattern(box, 4);
34180
34181             return;
34182             
34183         }
34184         
34185         Roo.each(boxes, function(box, k){
34186             
34187             if(!box.length){
34188                 return;
34189             }
34190             
34191             if(box.length == 1){
34192                 queue.push(box);
34193                 return;
34194             }
34195             
34196             filterPattern(box, 4);
34197             
34198         }, this);
34199         
34200         this._processVerticalLayoutQueue( queue, isInstant );
34201         
34202     },
34203     
34204 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34205 //    {
34206 //        if ( !items || !items.length ) {
34207 //            return;
34208 //        }
34209 //
34210 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34211 //        
34212 //    },
34213     
34214     _horizontalLayoutItems : function ( items , isInstant)
34215     {
34216         if ( !items || !items.length || items.length < 3) {
34217             return;
34218         }
34219         
34220         items.reverse();
34221         
34222         var eItems = items.slice(0, 3);
34223         
34224         items = items.slice(3, items.length);
34225         
34226         var standard = [
34227             ['xs', 'xs', 'xs', 'wide'],
34228             ['xs', 'xs', 'wide'],
34229             ['xs', 'xs', 'sm'],
34230             ['xs', 'xs', 'xs'],
34231             ['xs', 'wide'],
34232             ['xs', 'sm'],
34233             ['xs', 'xs'],
34234             ['xs'],
34235             
34236             ['sm', 'xs', 'xs'],
34237             ['sm', 'xs'],
34238             ['sm'],
34239             
34240             ['wide', 'xs', 'xs', 'xs'],
34241             ['wide', 'xs', 'xs'],
34242             ['wide', 'xs'],
34243             ['wide'],
34244             
34245             ['wide-thin']
34246         ];
34247         
34248         var queue = [];
34249         
34250         var boxes = [];
34251         
34252         var box = [];
34253         
34254         Roo.each(items, function(item, k){
34255             
34256             switch (item.size) {
34257                 case 'md' :
34258                 case 'md-left' :
34259                 case 'md-right' :
34260                 case 'tall' :
34261                     
34262                     if(box.length){
34263                         boxes.push(box);
34264                         box = [];
34265                     }
34266                     
34267                     boxes.push([item]);
34268                     
34269                     break;
34270                     
34271                 case 'xs' :
34272                 case 'sm' :
34273                 case 'wide' :
34274                 case 'wide-thin' :
34275                     
34276                     box.push(item);
34277                     
34278                     break;
34279                 default :
34280                     break;
34281                     
34282             }
34283             
34284         }, this);
34285         
34286         if(box.length){
34287             boxes.push(box);
34288             box = [];
34289         }
34290         
34291         var filterPattern = function(box, length)
34292         {
34293             if(!box.length){
34294                 return;
34295             }
34296             
34297             var match = false;
34298             
34299             var pattern = box.slice(0, length);
34300             
34301             var format = [];
34302             
34303             Roo.each(pattern, function(i){
34304                 format.push(i.size);
34305             }, this);
34306             
34307             Roo.each(standard, function(s){
34308                 
34309                 if(String(s) != String(format)){
34310                     return;
34311                 }
34312                 
34313                 match = true;
34314                 return false;
34315                 
34316             }, this);
34317             
34318             if(!match && length == 1){
34319                 return;
34320             }
34321             
34322             if(!match){
34323                 filterPattern(box, length - 1);
34324                 return;
34325             }
34326                 
34327             queue.push(pattern);
34328
34329             box = box.slice(length, box.length);
34330
34331             filterPattern(box, 4);
34332
34333             return;
34334             
34335         }
34336         
34337         Roo.each(boxes, function(box, k){
34338             
34339             if(!box.length){
34340                 return;
34341             }
34342             
34343             if(box.length == 1){
34344                 queue.push(box);
34345                 return;
34346             }
34347             
34348             filterPattern(box, 4);
34349             
34350         }, this);
34351         
34352         
34353         var prune = [];
34354         
34355         var pos = this.el.getBox(true);
34356         
34357         var minX = pos.x;
34358         
34359         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34360         
34361         var hit_end = false;
34362         
34363         Roo.each(queue, function(box){
34364             
34365             if(hit_end){
34366                 
34367                 Roo.each(box, function(b){
34368                 
34369                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34370                     b.el.hide();
34371
34372                 }, this);
34373
34374                 return;
34375             }
34376             
34377             var mx = 0;
34378             
34379             Roo.each(box, function(b){
34380                 
34381                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34382                 b.el.show();
34383
34384                 mx = Math.max(mx, b.x);
34385                 
34386             }, this);
34387             
34388             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34389             
34390             if(maxX < minX){
34391                 
34392                 Roo.each(box, function(b){
34393                 
34394                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34395                     b.el.hide();
34396                     
34397                 }, this);
34398                 
34399                 hit_end = true;
34400                 
34401                 return;
34402             }
34403             
34404             prune.push(box);
34405             
34406         }, this);
34407         
34408         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34409     },
34410     
34411     /** Sets position of item in DOM
34412     * @param {Element} item
34413     * @param {Number} x - horizontal position
34414     * @param {Number} y - vertical position
34415     * @param {Boolean} isInstant - disables transitions
34416     */
34417     _processVerticalLayoutQueue : function( queue, isInstant )
34418     {
34419         var pos = this.el.getBox(true);
34420         var x = pos.x;
34421         var y = pos.y;
34422         var maxY = [];
34423         
34424         for (var i = 0; i < this.cols; i++){
34425             maxY[i] = pos.y;
34426         }
34427         
34428         Roo.each(queue, function(box, k){
34429             
34430             var col = k % this.cols;
34431             
34432             Roo.each(box, function(b,kk){
34433                 
34434                 b.el.position('absolute');
34435                 
34436                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34437                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34438                 
34439                 if(b.size == 'md-left' || b.size == 'md-right'){
34440                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34441                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34442                 }
34443                 
34444                 b.el.setWidth(width);
34445                 b.el.setHeight(height);
34446                 // iframe?
34447                 b.el.select('iframe',true).setSize(width,height);
34448                 
34449             }, this);
34450             
34451             for (var i = 0; i < this.cols; i++){
34452                 
34453                 if(maxY[i] < maxY[col]){
34454                     col = i;
34455                     continue;
34456                 }
34457                 
34458                 col = Math.min(col, i);
34459                 
34460             }
34461             
34462             x = pos.x + col * (this.colWidth + this.padWidth);
34463             
34464             y = maxY[col];
34465             
34466             var positions = [];
34467             
34468             switch (box.length){
34469                 case 1 :
34470                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34471                     break;
34472                 case 2 :
34473                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34474                     break;
34475                 case 3 :
34476                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34477                     break;
34478                 case 4 :
34479                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34480                     break;
34481                 default :
34482                     break;
34483             }
34484             
34485             Roo.each(box, function(b,kk){
34486                 
34487                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34488                 
34489                 var sz = b.el.getSize();
34490                 
34491                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34492                 
34493             }, this);
34494             
34495         }, this);
34496         
34497         var mY = 0;
34498         
34499         for (var i = 0; i < this.cols; i++){
34500             mY = Math.max(mY, maxY[i]);
34501         }
34502         
34503         this.el.setHeight(mY - pos.y);
34504         
34505     },
34506     
34507 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34508 //    {
34509 //        var pos = this.el.getBox(true);
34510 //        var x = pos.x;
34511 //        var y = pos.y;
34512 //        var maxX = pos.right;
34513 //        
34514 //        var maxHeight = 0;
34515 //        
34516 //        Roo.each(items, function(item, k){
34517 //            
34518 //            var c = k % 2;
34519 //            
34520 //            item.el.position('absolute');
34521 //                
34522 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34523 //
34524 //            item.el.setWidth(width);
34525 //
34526 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34527 //
34528 //            item.el.setHeight(height);
34529 //            
34530 //            if(c == 0){
34531 //                item.el.setXY([x, y], isInstant ? false : true);
34532 //            } else {
34533 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34534 //            }
34535 //            
34536 //            y = y + height + this.alternativePadWidth;
34537 //            
34538 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34539 //            
34540 //        }, this);
34541 //        
34542 //        this.el.setHeight(maxHeight);
34543 //        
34544 //    },
34545     
34546     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34547     {
34548         var pos = this.el.getBox(true);
34549         
34550         var minX = pos.x;
34551         var minY = pos.y;
34552         
34553         var maxX = pos.right;
34554         
34555         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34556         
34557         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34558         
34559         Roo.each(queue, function(box, k){
34560             
34561             Roo.each(box, function(b, kk){
34562                 
34563                 b.el.position('absolute');
34564                 
34565                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34566                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34567                 
34568                 if(b.size == 'md-left' || b.size == 'md-right'){
34569                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34570                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34571                 }
34572                 
34573                 b.el.setWidth(width);
34574                 b.el.setHeight(height);
34575                 
34576             }, this);
34577             
34578             if(!box.length){
34579                 return;
34580             }
34581             
34582             var positions = [];
34583             
34584             switch (box.length){
34585                 case 1 :
34586                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34587                     break;
34588                 case 2 :
34589                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34590                     break;
34591                 case 3 :
34592                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34593                     break;
34594                 case 4 :
34595                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34596                     break;
34597                 default :
34598                     break;
34599             }
34600             
34601             Roo.each(box, function(b,kk){
34602                 
34603                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34604                 
34605                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34606                 
34607             }, this);
34608             
34609         }, this);
34610         
34611     },
34612     
34613     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34614     {
34615         Roo.each(eItems, function(b,k){
34616             
34617             b.size = (k == 0) ? 'sm' : 'xs';
34618             b.x = (k == 0) ? 2 : 1;
34619             b.y = (k == 0) ? 2 : 1;
34620             
34621             b.el.position('absolute');
34622             
34623             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34624                 
34625             b.el.setWidth(width);
34626             
34627             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34628             
34629             b.el.setHeight(height);
34630             
34631         }, this);
34632
34633         var positions = [];
34634         
34635         positions.push({
34636             x : maxX - this.unitWidth * 2 - this.gutter,
34637             y : minY
34638         });
34639         
34640         positions.push({
34641             x : maxX - this.unitWidth,
34642             y : minY + (this.unitWidth + this.gutter) * 2
34643         });
34644         
34645         positions.push({
34646             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34647             y : minY
34648         });
34649         
34650         Roo.each(eItems, function(b,k){
34651             
34652             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34653
34654         }, this);
34655         
34656     },
34657     
34658     getVerticalOneBoxColPositions : function(x, y, box)
34659     {
34660         var pos = [];
34661         
34662         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34663         
34664         if(box[0].size == 'md-left'){
34665             rand = 0;
34666         }
34667         
34668         if(box[0].size == 'md-right'){
34669             rand = 1;
34670         }
34671         
34672         pos.push({
34673             x : x + (this.unitWidth + this.gutter) * rand,
34674             y : y
34675         });
34676         
34677         return pos;
34678     },
34679     
34680     getVerticalTwoBoxColPositions : function(x, y, box)
34681     {
34682         var pos = [];
34683         
34684         if(box[0].size == 'xs'){
34685             
34686             pos.push({
34687                 x : x,
34688                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34689             });
34690
34691             pos.push({
34692                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34693                 y : y
34694             });
34695             
34696             return pos;
34697             
34698         }
34699         
34700         pos.push({
34701             x : x,
34702             y : y
34703         });
34704
34705         pos.push({
34706             x : x + (this.unitWidth + this.gutter) * 2,
34707             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34708         });
34709         
34710         return pos;
34711         
34712     },
34713     
34714     getVerticalThreeBoxColPositions : function(x, y, box)
34715     {
34716         var pos = [];
34717         
34718         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34719             
34720             pos.push({
34721                 x : x,
34722                 y : y
34723             });
34724
34725             pos.push({
34726                 x : x + (this.unitWidth + this.gutter) * 1,
34727                 y : y
34728             });
34729             
34730             pos.push({
34731                 x : x + (this.unitWidth + this.gutter) * 2,
34732                 y : y
34733             });
34734             
34735             return pos;
34736             
34737         }
34738         
34739         if(box[0].size == 'xs' && box[1].size == 'xs'){
34740             
34741             pos.push({
34742                 x : x,
34743                 y : y
34744             });
34745
34746             pos.push({
34747                 x : x,
34748                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34749             });
34750             
34751             pos.push({
34752                 x : x + (this.unitWidth + this.gutter) * 1,
34753                 y : y
34754             });
34755             
34756             return pos;
34757             
34758         }
34759         
34760         pos.push({
34761             x : x,
34762             y : y
34763         });
34764
34765         pos.push({
34766             x : x + (this.unitWidth + this.gutter) * 2,
34767             y : y
34768         });
34769
34770         pos.push({
34771             x : x + (this.unitWidth + this.gutter) * 2,
34772             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34773         });
34774             
34775         return pos;
34776         
34777     },
34778     
34779     getVerticalFourBoxColPositions : function(x, y, box)
34780     {
34781         var pos = [];
34782         
34783         if(box[0].size == 'xs'){
34784             
34785             pos.push({
34786                 x : x,
34787                 y : y
34788             });
34789
34790             pos.push({
34791                 x : x,
34792                 y : y + (this.unitHeight + this.gutter) * 1
34793             });
34794             
34795             pos.push({
34796                 x : x,
34797                 y : y + (this.unitHeight + this.gutter) * 2
34798             });
34799             
34800             pos.push({
34801                 x : x + (this.unitWidth + this.gutter) * 1,
34802                 y : y
34803             });
34804             
34805             return pos;
34806             
34807         }
34808         
34809         pos.push({
34810             x : x,
34811             y : y
34812         });
34813
34814         pos.push({
34815             x : x + (this.unitWidth + this.gutter) * 2,
34816             y : y
34817         });
34818
34819         pos.push({
34820             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34821             y : y + (this.unitHeight + this.gutter) * 1
34822         });
34823
34824         pos.push({
34825             x : x + (this.unitWidth + this.gutter) * 2,
34826             y : y + (this.unitWidth + this.gutter) * 2
34827         });
34828
34829         return pos;
34830         
34831     },
34832     
34833     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34834     {
34835         var pos = [];
34836         
34837         if(box[0].size == 'md-left'){
34838             pos.push({
34839                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34840                 y : minY
34841             });
34842             
34843             return pos;
34844         }
34845         
34846         if(box[0].size == 'md-right'){
34847             pos.push({
34848                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34849                 y : minY + (this.unitWidth + this.gutter) * 1
34850             });
34851             
34852             return pos;
34853         }
34854         
34855         var rand = Math.floor(Math.random() * (4 - box[0].y));
34856         
34857         pos.push({
34858             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34859             y : minY + (this.unitWidth + this.gutter) * rand
34860         });
34861         
34862         return pos;
34863         
34864     },
34865     
34866     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34867     {
34868         var pos = [];
34869         
34870         if(box[0].size == 'xs'){
34871             
34872             pos.push({
34873                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34874                 y : minY
34875             });
34876
34877             pos.push({
34878                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34879                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34880             });
34881             
34882             return pos;
34883             
34884         }
34885         
34886         pos.push({
34887             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34888             y : minY
34889         });
34890
34891         pos.push({
34892             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34893             y : minY + (this.unitWidth + this.gutter) * 2
34894         });
34895         
34896         return pos;
34897         
34898     },
34899     
34900     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34901     {
34902         var pos = [];
34903         
34904         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34905             
34906             pos.push({
34907                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34908                 y : minY
34909             });
34910
34911             pos.push({
34912                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34913                 y : minY + (this.unitWidth + this.gutter) * 1
34914             });
34915             
34916             pos.push({
34917                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34918                 y : minY + (this.unitWidth + this.gutter) * 2
34919             });
34920             
34921             return pos;
34922             
34923         }
34924         
34925         if(box[0].size == 'xs' && box[1].size == 'xs'){
34926             
34927             pos.push({
34928                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34929                 y : minY
34930             });
34931
34932             pos.push({
34933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34934                 y : minY
34935             });
34936             
34937             pos.push({
34938                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34939                 y : minY + (this.unitWidth + this.gutter) * 1
34940             });
34941             
34942             return pos;
34943             
34944         }
34945         
34946         pos.push({
34947             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34948             y : minY
34949         });
34950
34951         pos.push({
34952             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34953             y : minY + (this.unitWidth + this.gutter) * 2
34954         });
34955
34956         pos.push({
34957             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34958             y : minY + (this.unitWidth + this.gutter) * 2
34959         });
34960             
34961         return pos;
34962         
34963     },
34964     
34965     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34966     {
34967         var pos = [];
34968         
34969         if(box[0].size == 'xs'){
34970             
34971             pos.push({
34972                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34973                 y : minY
34974             });
34975
34976             pos.push({
34977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34978                 y : minY
34979             });
34980             
34981             pos.push({
34982                 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),
34983                 y : minY
34984             });
34985             
34986             pos.push({
34987                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34988                 y : minY + (this.unitWidth + this.gutter) * 1
34989             });
34990             
34991             return pos;
34992             
34993         }
34994         
34995         pos.push({
34996             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34997             y : minY
34998         });
34999         
35000         pos.push({
35001             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35002             y : minY + (this.unitWidth + this.gutter) * 2
35003         });
35004         
35005         pos.push({
35006             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35007             y : minY + (this.unitWidth + this.gutter) * 2
35008         });
35009         
35010         pos.push({
35011             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),
35012             y : minY + (this.unitWidth + this.gutter) * 2
35013         });
35014
35015         return pos;
35016         
35017     },
35018     
35019     /**
35020     * remove a Masonry Brick
35021     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35022     */
35023     removeBrick : function(brick_id)
35024     {
35025         if (!brick_id) {
35026             return;
35027         }
35028         
35029         for (var i = 0; i<this.bricks.length; i++) {
35030             if (this.bricks[i].id == brick_id) {
35031                 this.bricks.splice(i,1);
35032                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35033                 this.initial();
35034             }
35035         }
35036     },
35037     
35038     /**
35039     * adds a Masonry Brick
35040     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35041     */
35042     addBrick : function(cfg)
35043     {
35044         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35045         //this.register(cn);
35046         cn.parentId = this.id;
35047         cn.render(this.el);
35048         return cn;
35049     },
35050     
35051     /**
35052     * register a Masonry Brick
35053     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35054     */
35055     
35056     register : function(brick)
35057     {
35058         this.bricks.push(brick);
35059         brick.masonryId = this.id;
35060     },
35061     
35062     /**
35063     * clear all the Masonry Brick
35064     */
35065     clearAll : function()
35066     {
35067         this.bricks = [];
35068         //this.getChildContainer().dom.innerHTML = "";
35069         this.el.dom.innerHTML = '';
35070     },
35071     
35072     getSelected : function()
35073     {
35074         if (!this.selectedBrick) {
35075             return false;
35076         }
35077         
35078         return this.selectedBrick;
35079     }
35080 });
35081
35082 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35083     
35084     groups: {},
35085      /**
35086     * register a Masonry Layout
35087     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35088     */
35089     
35090     register : function(layout)
35091     {
35092         this.groups[layout.id] = layout;
35093     },
35094     /**
35095     * fetch a  Masonry Layout based on the masonry layout ID
35096     * @param {string} the masonry layout to add
35097     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35098     */
35099     
35100     get: function(layout_id) {
35101         if (typeof(this.groups[layout_id]) == 'undefined') {
35102             return false;
35103         }
35104         return this.groups[layout_id] ;
35105     }
35106     
35107     
35108     
35109 });
35110
35111  
35112
35113  /**
35114  *
35115  * This is based on 
35116  * http://masonry.desandro.com
35117  *
35118  * The idea is to render all the bricks based on vertical width...
35119  *
35120  * The original code extends 'outlayer' - we might need to use that....
35121  * 
35122  */
35123
35124
35125 /**
35126  * @class Roo.bootstrap.LayoutMasonryAuto
35127  * @extends Roo.bootstrap.Component
35128  * Bootstrap Layout Masonry class
35129  * 
35130  * @constructor
35131  * Create a new Element
35132  * @param {Object} config The config object
35133  */
35134
35135 Roo.bootstrap.LayoutMasonryAuto = function(config){
35136     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35137 };
35138
35139 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35140     
35141       /**
35142      * @cfg {Boolean} isFitWidth  - resize the width..
35143      */   
35144     isFitWidth : false,  // options..
35145     /**
35146      * @cfg {Boolean} isOriginLeft = left align?
35147      */   
35148     isOriginLeft : true,
35149     /**
35150      * @cfg {Boolean} isOriginTop = top align?
35151      */   
35152     isOriginTop : false,
35153     /**
35154      * @cfg {Boolean} isLayoutInstant = no animation?
35155      */   
35156     isLayoutInstant : false, // needed?
35157     /**
35158      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35159      */   
35160     isResizingContainer : true,
35161     /**
35162      * @cfg {Number} columnWidth  width of the columns 
35163      */   
35164     
35165     columnWidth : 0,
35166     
35167     /**
35168      * @cfg {Number} maxCols maximum number of columns
35169      */   
35170     
35171     maxCols: 0,
35172     /**
35173      * @cfg {Number} padHeight padding below box..
35174      */   
35175     
35176     padHeight : 10, 
35177     
35178     /**
35179      * @cfg {Boolean} isAutoInitial defalut true
35180      */   
35181     
35182     isAutoInitial : true, 
35183     
35184     // private?
35185     gutter : 0,
35186     
35187     containerWidth: 0,
35188     initialColumnWidth : 0,
35189     currentSize : null,
35190     
35191     colYs : null, // array.
35192     maxY : 0,
35193     padWidth: 10,
35194     
35195     
35196     tag: 'div',
35197     cls: '',
35198     bricks: null, //CompositeElement
35199     cols : 0, // array?
35200     // element : null, // wrapped now this.el
35201     _isLayoutInited : null, 
35202     
35203     
35204     getAutoCreate : function(){
35205         
35206         var cfg = {
35207             tag: this.tag,
35208             cls: 'blog-masonary-wrapper ' + this.cls,
35209             cn : {
35210                 cls : 'mas-boxes masonary'
35211             }
35212         };
35213         
35214         return cfg;
35215     },
35216     
35217     getChildContainer: function( )
35218     {
35219         if (this.boxesEl) {
35220             return this.boxesEl;
35221         }
35222         
35223         this.boxesEl = this.el.select('.mas-boxes').first();
35224         
35225         return this.boxesEl;
35226     },
35227     
35228     
35229     initEvents : function()
35230     {
35231         var _this = this;
35232         
35233         if(this.isAutoInitial){
35234             Roo.log('hook children rendered');
35235             this.on('childrenrendered', function() {
35236                 Roo.log('children rendered');
35237                 _this.initial();
35238             } ,this);
35239         }
35240         
35241     },
35242     
35243     initial : function()
35244     {
35245         this.reloadItems();
35246
35247         this.currentSize = this.el.getBox(true);
35248
35249         /// was window resize... - let's see if this works..
35250         Roo.EventManager.onWindowResize(this.resize, this); 
35251
35252         if(!this.isAutoInitial){
35253             this.layout();
35254             return;
35255         }
35256         
35257         this.layout.defer(500,this);
35258     },
35259     
35260     reloadItems: function()
35261     {
35262         this.bricks = this.el.select('.masonry-brick', true);
35263         
35264         this.bricks.each(function(b) {
35265             //Roo.log(b.getSize());
35266             if (!b.attr('originalwidth')) {
35267                 b.attr('originalwidth',  b.getSize().width);
35268             }
35269             
35270         });
35271         
35272         Roo.log(this.bricks.elements.length);
35273     },
35274     
35275     resize : function()
35276     {
35277         Roo.log('resize');
35278         var cs = this.el.getBox(true);
35279         
35280         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35281             Roo.log("no change in with or X");
35282             return;
35283         }
35284         this.currentSize = cs;
35285         this.layout();
35286     },
35287     
35288     layout : function()
35289     {
35290          Roo.log('layout');
35291         this._resetLayout();
35292         //this._manageStamps();
35293       
35294         // don't animate first layout
35295         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35296         this.layoutItems( isInstant );
35297       
35298         // flag for initalized
35299         this._isLayoutInited = true;
35300     },
35301     
35302     layoutItems : function( isInstant )
35303     {
35304         //var items = this._getItemsForLayout( this.items );
35305         // original code supports filtering layout items.. we just ignore it..
35306         
35307         this._layoutItems( this.bricks , isInstant );
35308       
35309         this._postLayout();
35310     },
35311     _layoutItems : function ( items , isInstant)
35312     {
35313        //this.fireEvent( 'layout', this, items );
35314     
35315
35316         if ( !items || !items.elements.length ) {
35317           // no items, emit event with empty array
35318             return;
35319         }
35320
35321         var queue = [];
35322         items.each(function(item) {
35323             Roo.log("layout item");
35324             Roo.log(item);
35325             // get x/y object from method
35326             var position = this._getItemLayoutPosition( item );
35327             // enqueue
35328             position.item = item;
35329             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35330             queue.push( position );
35331         }, this);
35332       
35333         this._processLayoutQueue( queue );
35334     },
35335     /** Sets position of item in DOM
35336     * @param {Element} item
35337     * @param {Number} x - horizontal position
35338     * @param {Number} y - vertical position
35339     * @param {Boolean} isInstant - disables transitions
35340     */
35341     _processLayoutQueue : function( queue )
35342     {
35343         for ( var i=0, len = queue.length; i < len; i++ ) {
35344             var obj = queue[i];
35345             obj.item.position('absolute');
35346             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35347         }
35348     },
35349       
35350     
35351     /**
35352     * Any logic you want to do after each layout,
35353     * i.e. size the container
35354     */
35355     _postLayout : function()
35356     {
35357         this.resizeContainer();
35358     },
35359     
35360     resizeContainer : function()
35361     {
35362         if ( !this.isResizingContainer ) {
35363             return;
35364         }
35365         var size = this._getContainerSize();
35366         if ( size ) {
35367             this.el.setSize(size.width,size.height);
35368             this.boxesEl.setSize(size.width,size.height);
35369         }
35370     },
35371     
35372     
35373     
35374     _resetLayout : function()
35375     {
35376         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35377         this.colWidth = this.el.getWidth();
35378         //this.gutter = this.el.getWidth(); 
35379         
35380         this.measureColumns();
35381
35382         // reset column Y
35383         var i = this.cols;
35384         this.colYs = [];
35385         while (i--) {
35386             this.colYs.push( 0 );
35387         }
35388     
35389         this.maxY = 0;
35390     },
35391
35392     measureColumns : function()
35393     {
35394         this.getContainerWidth();
35395       // if columnWidth is 0, default to outerWidth of first item
35396         if ( !this.columnWidth ) {
35397             var firstItem = this.bricks.first();
35398             Roo.log(firstItem);
35399             this.columnWidth  = this.containerWidth;
35400             if (firstItem && firstItem.attr('originalwidth') ) {
35401                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35402             }
35403             // columnWidth fall back to item of first element
35404             Roo.log("set column width?");
35405                         this.initialColumnWidth = this.columnWidth  ;
35406
35407             // if first elem has no width, default to size of container
35408             
35409         }
35410         
35411         
35412         if (this.initialColumnWidth) {
35413             this.columnWidth = this.initialColumnWidth;
35414         }
35415         
35416         
35417             
35418         // column width is fixed at the top - however if container width get's smaller we should
35419         // reduce it...
35420         
35421         // this bit calcs how man columns..
35422             
35423         var columnWidth = this.columnWidth += this.gutter;
35424       
35425         // calculate columns
35426         var containerWidth = this.containerWidth + this.gutter;
35427         
35428         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35429         // fix rounding errors, typically with gutters
35430         var excess = columnWidth - containerWidth % columnWidth;
35431         
35432         
35433         // if overshoot is less than a pixel, round up, otherwise floor it
35434         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35435         cols = Math[ mathMethod ]( cols );
35436         this.cols = Math.max( cols, 1 );
35437         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35438         
35439          // padding positioning..
35440         var totalColWidth = this.cols * this.columnWidth;
35441         var padavail = this.containerWidth - totalColWidth;
35442         // so for 2 columns - we need 3 'pads'
35443         
35444         var padNeeded = (1+this.cols) * this.padWidth;
35445         
35446         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35447         
35448         this.columnWidth += padExtra
35449         //this.padWidth = Math.floor(padavail /  ( this.cols));
35450         
35451         // adjust colum width so that padding is fixed??
35452         
35453         // we have 3 columns ... total = width * 3
35454         // we have X left over... that should be used by 
35455         
35456         //if (this.expandC) {
35457             
35458         //}
35459         
35460         
35461         
35462     },
35463     
35464     getContainerWidth : function()
35465     {
35466        /* // container is parent if fit width
35467         var container = this.isFitWidth ? this.element.parentNode : this.element;
35468         // check that this.size and size are there
35469         // IE8 triggers resize on body size change, so they might not be
35470         
35471         var size = getSize( container );  //FIXME
35472         this.containerWidth = size && size.innerWidth; //FIXME
35473         */
35474          
35475         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35476         
35477     },
35478     
35479     _getItemLayoutPosition : function( item )  // what is item?
35480     {
35481         // we resize the item to our columnWidth..
35482       
35483         item.setWidth(this.columnWidth);
35484         item.autoBoxAdjust  = false;
35485         
35486         var sz = item.getSize();
35487  
35488         // how many columns does this brick span
35489         var remainder = this.containerWidth % this.columnWidth;
35490         
35491         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35492         // round if off by 1 pixel, otherwise use ceil
35493         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35494         colSpan = Math.min( colSpan, this.cols );
35495         
35496         // normally this should be '1' as we dont' currently allow multi width columns..
35497         
35498         var colGroup = this._getColGroup( colSpan );
35499         // get the minimum Y value from the columns
35500         var minimumY = Math.min.apply( Math, colGroup );
35501         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35502         
35503         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35504          
35505         // position the brick
35506         var position = {
35507             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35508             y: this.currentSize.y + minimumY + this.padHeight
35509         };
35510         
35511         Roo.log(position);
35512         // apply setHeight to necessary columns
35513         var setHeight = minimumY + sz.height + this.padHeight;
35514         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35515         
35516         var setSpan = this.cols + 1 - colGroup.length;
35517         for ( var i = 0; i < setSpan; i++ ) {
35518           this.colYs[ shortColIndex + i ] = setHeight ;
35519         }
35520       
35521         return position;
35522     },
35523     
35524     /**
35525      * @param {Number} colSpan - number of columns the element spans
35526      * @returns {Array} colGroup
35527      */
35528     _getColGroup : function( colSpan )
35529     {
35530         if ( colSpan < 2 ) {
35531           // if brick spans only one column, use all the column Ys
35532           return this.colYs;
35533         }
35534       
35535         var colGroup = [];
35536         // how many different places could this brick fit horizontally
35537         var groupCount = this.cols + 1 - colSpan;
35538         // for each group potential horizontal position
35539         for ( var i = 0; i < groupCount; i++ ) {
35540           // make an array of colY values for that one group
35541           var groupColYs = this.colYs.slice( i, i + colSpan );
35542           // and get the max value of the array
35543           colGroup[i] = Math.max.apply( Math, groupColYs );
35544         }
35545         return colGroup;
35546     },
35547     /*
35548     _manageStamp : function( stamp )
35549     {
35550         var stampSize =  stamp.getSize();
35551         var offset = stamp.getBox();
35552         // get the columns that this stamp affects
35553         var firstX = this.isOriginLeft ? offset.x : offset.right;
35554         var lastX = firstX + stampSize.width;
35555         var firstCol = Math.floor( firstX / this.columnWidth );
35556         firstCol = Math.max( 0, firstCol );
35557         
35558         var lastCol = Math.floor( lastX / this.columnWidth );
35559         // lastCol should not go over if multiple of columnWidth #425
35560         lastCol -= lastX % this.columnWidth ? 0 : 1;
35561         lastCol = Math.min( this.cols - 1, lastCol );
35562         
35563         // set colYs to bottom of the stamp
35564         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35565             stampSize.height;
35566             
35567         for ( var i = firstCol; i <= lastCol; i++ ) {
35568           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35569         }
35570     },
35571     */
35572     
35573     _getContainerSize : function()
35574     {
35575         this.maxY = Math.max.apply( Math, this.colYs );
35576         var size = {
35577             height: this.maxY
35578         };
35579       
35580         if ( this.isFitWidth ) {
35581             size.width = this._getContainerFitWidth();
35582         }
35583       
35584         return size;
35585     },
35586     
35587     _getContainerFitWidth : function()
35588     {
35589         var unusedCols = 0;
35590         // count unused columns
35591         var i = this.cols;
35592         while ( --i ) {
35593           if ( this.colYs[i] !== 0 ) {
35594             break;
35595           }
35596           unusedCols++;
35597         }
35598         // fit container to columns that have been used
35599         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35600     },
35601     
35602     needsResizeLayout : function()
35603     {
35604         var previousWidth = this.containerWidth;
35605         this.getContainerWidth();
35606         return previousWidth !== this.containerWidth;
35607     }
35608  
35609 });
35610
35611  
35612
35613  /*
35614  * - LGPL
35615  *
35616  * element
35617  * 
35618  */
35619
35620 /**
35621  * @class Roo.bootstrap.MasonryBrick
35622  * @extends Roo.bootstrap.Component
35623  * Bootstrap MasonryBrick class
35624  * 
35625  * @constructor
35626  * Create a new MasonryBrick
35627  * @param {Object} config The config object
35628  */
35629
35630 Roo.bootstrap.MasonryBrick = function(config){
35631     
35632     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35633     
35634     Roo.bootstrap.MasonryBrick.register(this);
35635     
35636     this.addEvents({
35637         // raw events
35638         /**
35639          * @event click
35640          * When a MasonryBrick is clcik
35641          * @param {Roo.bootstrap.MasonryBrick} this
35642          * @param {Roo.EventObject} e
35643          */
35644         "click" : true
35645     });
35646 };
35647
35648 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35649     
35650     /**
35651      * @cfg {String} title
35652      */   
35653     title : '',
35654     /**
35655      * @cfg {String} html
35656      */   
35657     html : '',
35658     /**
35659      * @cfg {String} bgimage
35660      */   
35661     bgimage : '',
35662     /**
35663      * @cfg {String} videourl
35664      */   
35665     videourl : '',
35666     /**
35667      * @cfg {String} cls
35668      */   
35669     cls : '',
35670     /**
35671      * @cfg {String} href
35672      */   
35673     href : '',
35674     /**
35675      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35676      */   
35677     size : 'xs',
35678     
35679     /**
35680      * @cfg {String} placetitle (center|bottom)
35681      */   
35682     placetitle : '',
35683     
35684     /**
35685      * @cfg {Boolean} isFitContainer defalut true
35686      */   
35687     isFitContainer : true, 
35688     
35689     /**
35690      * @cfg {Boolean} preventDefault defalut false
35691      */   
35692     preventDefault : false, 
35693     
35694     /**
35695      * @cfg {Boolean} inverse defalut false
35696      */   
35697     maskInverse : false, 
35698     
35699     getAutoCreate : function()
35700     {
35701         if(!this.isFitContainer){
35702             return this.getSplitAutoCreate();
35703         }
35704         
35705         var cls = 'masonry-brick masonry-brick-full';
35706         
35707         if(this.href.length){
35708             cls += ' masonry-brick-link';
35709         }
35710         
35711         if(this.bgimage.length){
35712             cls += ' masonry-brick-image';
35713         }
35714         
35715         if(this.maskInverse){
35716             cls += ' mask-inverse';
35717         }
35718         
35719         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35720             cls += ' enable-mask';
35721         }
35722         
35723         if(this.size){
35724             cls += ' masonry-' + this.size + '-brick';
35725         }
35726         
35727         if(this.placetitle.length){
35728             
35729             switch (this.placetitle) {
35730                 case 'center' :
35731                     cls += ' masonry-center-title';
35732                     break;
35733                 case 'bottom' :
35734                     cls += ' masonry-bottom-title';
35735                     break;
35736                 default:
35737                     break;
35738             }
35739             
35740         } else {
35741             if(!this.html.length && !this.bgimage.length){
35742                 cls += ' masonry-center-title';
35743             }
35744
35745             if(!this.html.length && this.bgimage.length){
35746                 cls += ' masonry-bottom-title';
35747             }
35748         }
35749         
35750         if(this.cls){
35751             cls += ' ' + this.cls;
35752         }
35753         
35754         var cfg = {
35755             tag: (this.href.length) ? 'a' : 'div',
35756             cls: cls,
35757             cn: [
35758                 {
35759                     tag: 'div',
35760                     cls: 'masonry-brick-mask'
35761                 },
35762                 {
35763                     tag: 'div',
35764                     cls: 'masonry-brick-paragraph',
35765                     cn: []
35766                 }
35767             ]
35768         };
35769         
35770         if(this.href.length){
35771             cfg.href = this.href;
35772         }
35773         
35774         var cn = cfg.cn[1].cn;
35775         
35776         if(this.title.length){
35777             cn.push({
35778                 tag: 'h4',
35779                 cls: 'masonry-brick-title',
35780                 html: this.title
35781             });
35782         }
35783         
35784         if(this.html.length){
35785             cn.push({
35786                 tag: 'p',
35787                 cls: 'masonry-brick-text',
35788                 html: this.html
35789             });
35790         }
35791         
35792         if (!this.title.length && !this.html.length) {
35793             cfg.cn[1].cls += ' hide';
35794         }
35795         
35796         if(this.bgimage.length){
35797             cfg.cn.push({
35798                 tag: 'img',
35799                 cls: 'masonry-brick-image-view',
35800                 src: this.bgimage
35801             });
35802         }
35803         
35804         if(this.videourl.length){
35805             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35806             // youtube support only?
35807             cfg.cn.push({
35808                 tag: 'iframe',
35809                 cls: 'masonry-brick-image-view',
35810                 src: vurl,
35811                 frameborder : 0,
35812                 allowfullscreen : true
35813             });
35814         }
35815         
35816         return cfg;
35817         
35818     },
35819     
35820     getSplitAutoCreate : function()
35821     {
35822         var cls = 'masonry-brick masonry-brick-split';
35823         
35824         if(this.href.length){
35825             cls += ' masonry-brick-link';
35826         }
35827         
35828         if(this.bgimage.length){
35829             cls += ' masonry-brick-image';
35830         }
35831         
35832         if(this.size){
35833             cls += ' masonry-' + this.size + '-brick';
35834         }
35835         
35836         switch (this.placetitle) {
35837             case 'center' :
35838                 cls += ' masonry-center-title';
35839                 break;
35840             case 'bottom' :
35841                 cls += ' masonry-bottom-title';
35842                 break;
35843             default:
35844                 if(!this.bgimage.length){
35845                     cls += ' masonry-center-title';
35846                 }
35847
35848                 if(this.bgimage.length){
35849                     cls += ' masonry-bottom-title';
35850                 }
35851                 break;
35852         }
35853         
35854         if(this.cls){
35855             cls += ' ' + this.cls;
35856         }
35857         
35858         var cfg = {
35859             tag: (this.href.length) ? 'a' : 'div',
35860             cls: cls,
35861             cn: [
35862                 {
35863                     tag: 'div',
35864                     cls: 'masonry-brick-split-head',
35865                     cn: [
35866                         {
35867                             tag: 'div',
35868                             cls: 'masonry-brick-paragraph',
35869                             cn: []
35870                         }
35871                     ]
35872                 },
35873                 {
35874                     tag: 'div',
35875                     cls: 'masonry-brick-split-body',
35876                     cn: []
35877                 }
35878             ]
35879         };
35880         
35881         if(this.href.length){
35882             cfg.href = this.href;
35883         }
35884         
35885         if(this.title.length){
35886             cfg.cn[0].cn[0].cn.push({
35887                 tag: 'h4',
35888                 cls: 'masonry-brick-title',
35889                 html: this.title
35890             });
35891         }
35892         
35893         if(this.html.length){
35894             cfg.cn[1].cn.push({
35895                 tag: 'p',
35896                 cls: 'masonry-brick-text',
35897                 html: this.html
35898             });
35899         }
35900
35901         if(this.bgimage.length){
35902             cfg.cn[0].cn.push({
35903                 tag: 'img',
35904                 cls: 'masonry-brick-image-view',
35905                 src: this.bgimage
35906             });
35907         }
35908         
35909         if(this.videourl.length){
35910             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35911             // youtube support only?
35912             cfg.cn[0].cn.cn.push({
35913                 tag: 'iframe',
35914                 cls: 'masonry-brick-image-view',
35915                 src: vurl,
35916                 frameborder : 0,
35917                 allowfullscreen : true
35918             });
35919         }
35920         
35921         return cfg;
35922     },
35923     
35924     initEvents: function() 
35925     {
35926         switch (this.size) {
35927             case 'xs' :
35928                 this.x = 1;
35929                 this.y = 1;
35930                 break;
35931             case 'sm' :
35932                 this.x = 2;
35933                 this.y = 2;
35934                 break;
35935             case 'md' :
35936             case 'md-left' :
35937             case 'md-right' :
35938                 this.x = 3;
35939                 this.y = 3;
35940                 break;
35941             case 'tall' :
35942                 this.x = 2;
35943                 this.y = 3;
35944                 break;
35945             case 'wide' :
35946                 this.x = 3;
35947                 this.y = 2;
35948                 break;
35949             case 'wide-thin' :
35950                 this.x = 3;
35951                 this.y = 1;
35952                 break;
35953                         
35954             default :
35955                 break;
35956         }
35957         
35958         if(Roo.isTouch){
35959             this.el.on('touchstart', this.onTouchStart, this);
35960             this.el.on('touchmove', this.onTouchMove, this);
35961             this.el.on('touchend', this.onTouchEnd, this);
35962             this.el.on('contextmenu', this.onContextMenu, this);
35963         } else {
35964             this.el.on('mouseenter'  ,this.enter, this);
35965             this.el.on('mouseleave', this.leave, this);
35966             this.el.on('click', this.onClick, this);
35967         }
35968         
35969         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35970             this.parent().bricks.push(this);   
35971         }
35972         
35973     },
35974     
35975     onClick: function(e, el)
35976     {
35977         var time = this.endTimer - this.startTimer;
35978         // Roo.log(e.preventDefault());
35979         if(Roo.isTouch){
35980             if(time > 1000){
35981                 e.preventDefault();
35982                 return;
35983             }
35984         }
35985         
35986         if(!this.preventDefault){
35987             return;
35988         }
35989         
35990         e.preventDefault();
35991         
35992         if (this.activeClass != '') {
35993             this.selectBrick();
35994         }
35995         
35996         this.fireEvent('click', this, e);
35997     },
35998     
35999     enter: function(e, el)
36000     {
36001         e.preventDefault();
36002         
36003         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36004             return;
36005         }
36006         
36007         if(this.bgimage.length && this.html.length){
36008             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36009         }
36010     },
36011     
36012     leave: function(e, el)
36013     {
36014         e.preventDefault();
36015         
36016         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36017             return;
36018         }
36019         
36020         if(this.bgimage.length && this.html.length){
36021             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36022         }
36023     },
36024     
36025     onTouchStart: function(e, el)
36026     {
36027 //        e.preventDefault();
36028         
36029         this.touchmoved = false;
36030         
36031         if(!this.isFitContainer){
36032             return;
36033         }
36034         
36035         if(!this.bgimage.length || !this.html.length){
36036             return;
36037         }
36038         
36039         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36040         
36041         this.timer = new Date().getTime();
36042         
36043     },
36044     
36045     onTouchMove: function(e, el)
36046     {
36047         this.touchmoved = true;
36048     },
36049     
36050     onContextMenu : function(e,el)
36051     {
36052         e.preventDefault();
36053         e.stopPropagation();
36054         return false;
36055     },
36056     
36057     onTouchEnd: function(e, el)
36058     {
36059 //        e.preventDefault();
36060         
36061         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36062         
36063             this.leave(e,el);
36064             
36065             return;
36066         }
36067         
36068         if(!this.bgimage.length || !this.html.length){
36069             
36070             if(this.href.length){
36071                 window.location.href = this.href;
36072             }
36073             
36074             return;
36075         }
36076         
36077         if(!this.isFitContainer){
36078             return;
36079         }
36080         
36081         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36082         
36083         window.location.href = this.href;
36084     },
36085     
36086     //selection on single brick only
36087     selectBrick : function() {
36088         
36089         if (!this.parentId) {
36090             return;
36091         }
36092         
36093         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36094         var index = m.selectedBrick.indexOf(this.id);
36095         
36096         if ( index > -1) {
36097             m.selectedBrick.splice(index,1);
36098             this.el.removeClass(this.activeClass);
36099             return;
36100         }
36101         
36102         for(var i = 0; i < m.selectedBrick.length; i++) {
36103             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36104             b.el.removeClass(b.activeClass);
36105         }
36106         
36107         m.selectedBrick = [];
36108         
36109         m.selectedBrick.push(this.id);
36110         this.el.addClass(this.activeClass);
36111         return;
36112     },
36113     
36114     isSelected : function(){
36115         return this.el.hasClass(this.activeClass);
36116         
36117     }
36118 });
36119
36120 Roo.apply(Roo.bootstrap.MasonryBrick, {
36121     
36122     //groups: {},
36123     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36124      /**
36125     * register a Masonry Brick
36126     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36127     */
36128     
36129     register : function(brick)
36130     {
36131         //this.groups[brick.id] = brick;
36132         this.groups.add(brick.id, brick);
36133     },
36134     /**
36135     * fetch a  masonry brick based on the masonry brick ID
36136     * @param {string} the masonry brick to add
36137     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36138     */
36139     
36140     get: function(brick_id) 
36141     {
36142         // if (typeof(this.groups[brick_id]) == 'undefined') {
36143         //     return false;
36144         // }
36145         // return this.groups[brick_id] ;
36146         
36147         if(this.groups.key(brick_id)) {
36148             return this.groups.key(brick_id);
36149         }
36150         
36151         return false;
36152     }
36153     
36154     
36155     
36156 });
36157
36158  /*
36159  * - LGPL
36160  *
36161  * element
36162  * 
36163  */
36164
36165 /**
36166  * @class Roo.bootstrap.Brick
36167  * @extends Roo.bootstrap.Component
36168  * Bootstrap Brick class
36169  * 
36170  * @constructor
36171  * Create a new Brick
36172  * @param {Object} config The config object
36173  */
36174
36175 Roo.bootstrap.Brick = function(config){
36176     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36177     
36178     this.addEvents({
36179         // raw events
36180         /**
36181          * @event click
36182          * When a Brick is click
36183          * @param {Roo.bootstrap.Brick} this
36184          * @param {Roo.EventObject} e
36185          */
36186         "click" : true
36187     });
36188 };
36189
36190 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36191     
36192     /**
36193      * @cfg {String} title
36194      */   
36195     title : '',
36196     /**
36197      * @cfg {String} html
36198      */   
36199     html : '',
36200     /**
36201      * @cfg {String} bgimage
36202      */   
36203     bgimage : '',
36204     /**
36205      * @cfg {String} cls
36206      */   
36207     cls : '',
36208     /**
36209      * @cfg {String} href
36210      */   
36211     href : '',
36212     /**
36213      * @cfg {String} video
36214      */   
36215     video : '',
36216     /**
36217      * @cfg {Boolean} square
36218      */   
36219     square : true,
36220     
36221     getAutoCreate : function()
36222     {
36223         var cls = 'roo-brick';
36224         
36225         if(this.href.length){
36226             cls += ' roo-brick-link';
36227         }
36228         
36229         if(this.bgimage.length){
36230             cls += ' roo-brick-image';
36231         }
36232         
36233         if(!this.html.length && !this.bgimage.length){
36234             cls += ' roo-brick-center-title';
36235         }
36236         
36237         if(!this.html.length && this.bgimage.length){
36238             cls += ' roo-brick-bottom-title';
36239         }
36240         
36241         if(this.cls){
36242             cls += ' ' + this.cls;
36243         }
36244         
36245         var cfg = {
36246             tag: (this.href.length) ? 'a' : 'div',
36247             cls: cls,
36248             cn: [
36249                 {
36250                     tag: 'div',
36251                     cls: 'roo-brick-paragraph',
36252                     cn: []
36253                 }
36254             ]
36255         };
36256         
36257         if(this.href.length){
36258             cfg.href = this.href;
36259         }
36260         
36261         var cn = cfg.cn[0].cn;
36262         
36263         if(this.title.length){
36264             cn.push({
36265                 tag: 'h4',
36266                 cls: 'roo-brick-title',
36267                 html: this.title
36268             });
36269         }
36270         
36271         if(this.html.length){
36272             cn.push({
36273                 tag: 'p',
36274                 cls: 'roo-brick-text',
36275                 html: this.html
36276             });
36277         } else {
36278             cn.cls += ' hide';
36279         }
36280         
36281         if(this.bgimage.length){
36282             cfg.cn.push({
36283                 tag: 'img',
36284                 cls: 'roo-brick-image-view',
36285                 src: this.bgimage
36286             });
36287         }
36288         
36289         return cfg;
36290     },
36291     
36292     initEvents: function() 
36293     {
36294         if(this.title.length || this.html.length){
36295             this.el.on('mouseenter'  ,this.enter, this);
36296             this.el.on('mouseleave', this.leave, this);
36297         }
36298         
36299         Roo.EventManager.onWindowResize(this.resize, this); 
36300         
36301         if(this.bgimage.length){
36302             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36303             this.imageEl.on('load', this.onImageLoad, this);
36304             return;
36305         }
36306         
36307         this.resize();
36308     },
36309     
36310     onImageLoad : function()
36311     {
36312         this.resize();
36313     },
36314     
36315     resize : function()
36316     {
36317         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36318         
36319         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36320         
36321         if(this.bgimage.length){
36322             var image = this.el.select('.roo-brick-image-view', true).first();
36323             
36324             image.setWidth(paragraph.getWidth());
36325             
36326             if(this.square){
36327                 image.setHeight(paragraph.getWidth());
36328             }
36329             
36330             this.el.setHeight(image.getHeight());
36331             paragraph.setHeight(image.getHeight());
36332             
36333         }
36334         
36335     },
36336     
36337     enter: function(e, el)
36338     {
36339         e.preventDefault();
36340         
36341         if(this.bgimage.length){
36342             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36343             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36344         }
36345     },
36346     
36347     leave: function(e, el)
36348     {
36349         e.preventDefault();
36350         
36351         if(this.bgimage.length){
36352             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36353             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36354         }
36355     }
36356     
36357 });
36358
36359  
36360
36361  /*
36362  * - LGPL
36363  *
36364  * Number field 
36365  */
36366
36367 /**
36368  * @class Roo.bootstrap.NumberField
36369  * @extends Roo.bootstrap.Input
36370  * Bootstrap NumberField class
36371  * 
36372  * 
36373  * 
36374  * 
36375  * @constructor
36376  * Create a new NumberField
36377  * @param {Object} config The config object
36378  */
36379
36380 Roo.bootstrap.NumberField = function(config){
36381     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36382 };
36383
36384 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36385     
36386     /**
36387      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36388      */
36389     allowDecimals : true,
36390     /**
36391      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36392      */
36393     decimalSeparator : ".",
36394     /**
36395      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36396      */
36397     decimalPrecision : 2,
36398     /**
36399      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36400      */
36401     allowNegative : true,
36402     
36403     /**
36404      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36405      */
36406     allowZero: true,
36407     /**
36408      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36409      */
36410     minValue : Number.NEGATIVE_INFINITY,
36411     /**
36412      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36413      */
36414     maxValue : Number.MAX_VALUE,
36415     /**
36416      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36417      */
36418     minText : "The minimum value for this field is {0}",
36419     /**
36420      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36421      */
36422     maxText : "The maximum value for this field is {0}",
36423     /**
36424      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36425      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36426      */
36427     nanText : "{0} is not a valid number",
36428     /**
36429      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36430      */
36431     thousandsDelimiter : false,
36432     /**
36433      * @cfg {String} valueAlign alignment of value
36434      */
36435     valueAlign : "left",
36436
36437     getAutoCreate : function()
36438     {
36439         var hiddenInput = {
36440             tag: 'input',
36441             type: 'hidden',
36442             id: Roo.id(),
36443             cls: 'hidden-number-input'
36444         };
36445         
36446         if (this.name) {
36447             hiddenInput.name = this.name;
36448         }
36449         
36450         this.name = '';
36451         
36452         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36453         
36454         this.name = hiddenInput.name;
36455         
36456         if(cfg.cn.length > 0) {
36457             cfg.cn.push(hiddenInput);
36458         }
36459         
36460         return cfg;
36461     },
36462
36463     // private
36464     initEvents : function()
36465     {   
36466         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36467         
36468         var allowed = "0123456789";
36469         
36470         if(this.allowDecimals){
36471             allowed += this.decimalSeparator;
36472         }
36473         
36474         if(this.allowNegative){
36475             allowed += "-";
36476         }
36477         
36478         if(this.thousandsDelimiter) {
36479             allowed += ",";
36480         }
36481         
36482         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36483         
36484         var keyPress = function(e){
36485             
36486             var k = e.getKey();
36487             
36488             var c = e.getCharCode();
36489             
36490             if(
36491                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36492                     allowed.indexOf(String.fromCharCode(c)) === -1
36493             ){
36494                 e.stopEvent();
36495                 return;
36496             }
36497             
36498             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36499                 return;
36500             }
36501             
36502             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36503                 e.stopEvent();
36504             }
36505         };
36506         
36507         this.el.on("keypress", keyPress, this);
36508     },
36509     
36510     validateValue : function(value)
36511     {
36512         
36513         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36514             return false;
36515         }
36516         
36517         var num = this.parseValue(value);
36518         
36519         if(isNaN(num)){
36520             this.markInvalid(String.format(this.nanText, value));
36521             return false;
36522         }
36523         
36524         if(num < this.minValue){
36525             this.markInvalid(String.format(this.minText, this.minValue));
36526             return false;
36527         }
36528         
36529         if(num > this.maxValue){
36530             this.markInvalid(String.format(this.maxText, this.maxValue));
36531             return false;
36532         }
36533         
36534         return true;
36535     },
36536
36537     getValue : function()
36538     {
36539         var v = this.hiddenEl().getValue();
36540         
36541         return this.fixPrecision(this.parseValue(v));
36542     },
36543
36544     parseValue : function(value)
36545     {
36546         if(this.thousandsDelimiter) {
36547             value += "";
36548             r = new RegExp(",", "g");
36549             value = value.replace(r, "");
36550         }
36551         
36552         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36553         return isNaN(value) ? '' : value;
36554     },
36555
36556     fixPrecision : function(value)
36557     {
36558         if(this.thousandsDelimiter) {
36559             value += "";
36560             r = new RegExp(",", "g");
36561             value = value.replace(r, "");
36562         }
36563         
36564         var nan = isNaN(value);
36565         
36566         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36567             return nan ? '' : value;
36568         }
36569         return parseFloat(value).toFixed(this.decimalPrecision);
36570     },
36571
36572     setValue : function(v)
36573     {
36574         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36575         
36576         this.value = v;
36577         
36578         if(this.rendered){
36579             
36580             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36581             
36582             this.inputEl().dom.value = (v == '') ? '' :
36583                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36584             
36585             if(!this.allowZero && v === '0') {
36586                 this.hiddenEl().dom.value = '';
36587                 this.inputEl().dom.value = '';
36588             }
36589             
36590             this.validate();
36591         }
36592     },
36593
36594     decimalPrecisionFcn : function(v)
36595     {
36596         return Math.floor(v);
36597     },
36598
36599     beforeBlur : function()
36600     {
36601         var v = this.parseValue(this.getRawValue());
36602         
36603         if(v || v === 0 || v === ''){
36604             this.setValue(v);
36605         }
36606     },
36607     
36608     hiddenEl : function()
36609     {
36610         return this.el.select('input.hidden-number-input',true).first();
36611     }
36612     
36613 });
36614
36615  
36616
36617 /*
36618 * Licence: LGPL
36619 */
36620
36621 /**
36622  * @class Roo.bootstrap.DocumentSlider
36623  * @extends Roo.bootstrap.Component
36624  * Bootstrap DocumentSlider class
36625  * 
36626  * @constructor
36627  * Create a new DocumentViewer
36628  * @param {Object} config The config object
36629  */
36630
36631 Roo.bootstrap.DocumentSlider = function(config){
36632     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36633     
36634     this.files = [];
36635     
36636     this.addEvents({
36637         /**
36638          * @event initial
36639          * Fire after initEvent
36640          * @param {Roo.bootstrap.DocumentSlider} this
36641          */
36642         "initial" : true,
36643         /**
36644          * @event update
36645          * Fire after update
36646          * @param {Roo.bootstrap.DocumentSlider} this
36647          */
36648         "update" : true,
36649         /**
36650          * @event click
36651          * Fire after click
36652          * @param {Roo.bootstrap.DocumentSlider} this
36653          */
36654         "click" : true
36655     });
36656 };
36657
36658 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36659     
36660     files : false,
36661     
36662     indicator : 0,
36663     
36664     getAutoCreate : function()
36665     {
36666         var cfg = {
36667             tag : 'div',
36668             cls : 'roo-document-slider',
36669             cn : [
36670                 {
36671                     tag : 'div',
36672                     cls : 'roo-document-slider-header',
36673                     cn : [
36674                         {
36675                             tag : 'div',
36676                             cls : 'roo-document-slider-header-title'
36677                         }
36678                     ]
36679                 },
36680                 {
36681                     tag : 'div',
36682                     cls : 'roo-document-slider-body',
36683                     cn : [
36684                         {
36685                             tag : 'div',
36686                             cls : 'roo-document-slider-prev',
36687                             cn : [
36688                                 {
36689                                     tag : 'i',
36690                                     cls : 'fa fa-chevron-left'
36691                                 }
36692                             ]
36693                         },
36694                         {
36695                             tag : 'div',
36696                             cls : 'roo-document-slider-thumb',
36697                             cn : [
36698                                 {
36699                                     tag : 'img',
36700                                     cls : 'roo-document-slider-image'
36701                                 }
36702                             ]
36703                         },
36704                         {
36705                             tag : 'div',
36706                             cls : 'roo-document-slider-next',
36707                             cn : [
36708                                 {
36709                                     tag : 'i',
36710                                     cls : 'fa fa-chevron-right'
36711                                 }
36712                             ]
36713                         }
36714                     ]
36715                 }
36716             ]
36717         };
36718         
36719         return cfg;
36720     },
36721     
36722     initEvents : function()
36723     {
36724         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36725         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36726         
36727         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36728         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36729         
36730         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36731         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36732         
36733         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36734         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36735         
36736         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36737         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36738         
36739         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36740         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36741         
36742         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36743         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36744         
36745         this.thumbEl.on('click', this.onClick, this);
36746         
36747         this.prevIndicator.on('click', this.prev, this);
36748         
36749         this.nextIndicator.on('click', this.next, this);
36750         
36751     },
36752     
36753     initial : function()
36754     {
36755         if(this.files.length){
36756             this.indicator = 1;
36757             this.update()
36758         }
36759         
36760         this.fireEvent('initial', this);
36761     },
36762     
36763     update : function()
36764     {
36765         this.imageEl.attr('src', this.files[this.indicator - 1]);
36766         
36767         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36768         
36769         this.prevIndicator.show();
36770         
36771         if(this.indicator == 1){
36772             this.prevIndicator.hide();
36773         }
36774         
36775         this.nextIndicator.show();
36776         
36777         if(this.indicator == this.files.length){
36778             this.nextIndicator.hide();
36779         }
36780         
36781         this.thumbEl.scrollTo('top');
36782         
36783         this.fireEvent('update', this);
36784     },
36785     
36786     onClick : function(e)
36787     {
36788         e.preventDefault();
36789         
36790         this.fireEvent('click', this);
36791     },
36792     
36793     prev : function(e)
36794     {
36795         e.preventDefault();
36796         
36797         this.indicator = Math.max(1, this.indicator - 1);
36798         
36799         this.update();
36800     },
36801     
36802     next : function(e)
36803     {
36804         e.preventDefault();
36805         
36806         this.indicator = Math.min(this.files.length, this.indicator + 1);
36807         
36808         this.update();
36809     }
36810 });
36811 /*
36812  * - LGPL
36813  *
36814  * RadioSet
36815  *
36816  *
36817  */
36818
36819 /**
36820  * @class Roo.bootstrap.RadioSet
36821  * @extends Roo.bootstrap.Input
36822  * Bootstrap RadioSet class
36823  * @cfg {String} indicatorpos (left|right) default left
36824  * @cfg {Boolean} inline (true|false) inline the element (default true)
36825  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36826  * @constructor
36827  * Create a new RadioSet
36828  * @param {Object} config The config object
36829  */
36830
36831 Roo.bootstrap.RadioSet = function(config){
36832     
36833     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36834     
36835     this.radioes = [];
36836     
36837     Roo.bootstrap.RadioSet.register(this);
36838     
36839     this.addEvents({
36840         /**
36841         * @event check
36842         * Fires when the element is checked or unchecked.
36843         * @param {Roo.bootstrap.RadioSet} this This radio
36844         * @param {Roo.bootstrap.Radio} item The checked item
36845         */
36846        check : true,
36847        /**
36848         * @event click
36849         * Fires when the element is click.
36850         * @param {Roo.bootstrap.RadioSet} this This radio set
36851         * @param {Roo.bootstrap.Radio} item The checked item
36852         * @param {Roo.EventObject} e The event object
36853         */
36854        click : true
36855     });
36856     
36857 };
36858
36859 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36860
36861     radioes : false,
36862     
36863     inline : true,
36864     
36865     weight : '',
36866     
36867     indicatorpos : 'left',
36868     
36869     getAutoCreate : function()
36870     {
36871         var label = {
36872             tag : 'label',
36873             cls : 'roo-radio-set-label',
36874             cn : [
36875                 {
36876                     tag : 'span',
36877                     html : this.fieldLabel
36878                 }
36879             ]
36880         };
36881         if (Roo.bootstrap.version == 3) {
36882             
36883             
36884             if(this.indicatorpos == 'left'){
36885                 label.cn.unshift({
36886                     tag : 'i',
36887                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36888                     tooltip : 'This field is required'
36889                 });
36890             } else {
36891                 label.cn.push({
36892                     tag : 'i',
36893                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36894                     tooltip : 'This field is required'
36895                 });
36896             }
36897         }
36898         var items = {
36899             tag : 'div',
36900             cls : 'roo-radio-set-items'
36901         };
36902         
36903         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36904         
36905         if (align === 'left' && this.fieldLabel.length) {
36906             
36907             items = {
36908                 cls : "roo-radio-set-right", 
36909                 cn: [
36910                     items
36911                 ]
36912             };
36913             
36914             if(this.labelWidth > 12){
36915                 label.style = "width: " + this.labelWidth + 'px';
36916             }
36917             
36918             if(this.labelWidth < 13 && this.labelmd == 0){
36919                 this.labelmd = this.labelWidth;
36920             }
36921             
36922             if(this.labellg > 0){
36923                 label.cls += ' col-lg-' + this.labellg;
36924                 items.cls += ' col-lg-' + (12 - this.labellg);
36925             }
36926             
36927             if(this.labelmd > 0){
36928                 label.cls += ' col-md-' + this.labelmd;
36929                 items.cls += ' col-md-' + (12 - this.labelmd);
36930             }
36931             
36932             if(this.labelsm > 0){
36933                 label.cls += ' col-sm-' + this.labelsm;
36934                 items.cls += ' col-sm-' + (12 - this.labelsm);
36935             }
36936             
36937             if(this.labelxs > 0){
36938                 label.cls += ' col-xs-' + this.labelxs;
36939                 items.cls += ' col-xs-' + (12 - this.labelxs);
36940             }
36941         }
36942         
36943         var cfg = {
36944             tag : 'div',
36945             cls : 'roo-radio-set',
36946             cn : [
36947                 {
36948                     tag : 'input',
36949                     cls : 'roo-radio-set-input',
36950                     type : 'hidden',
36951                     name : this.name,
36952                     value : this.value ? this.value :  ''
36953                 },
36954                 label,
36955                 items
36956             ]
36957         };
36958         
36959         if(this.weight.length){
36960             cfg.cls += ' roo-radio-' + this.weight;
36961         }
36962         
36963         if(this.inline) {
36964             cfg.cls += ' roo-radio-set-inline';
36965         }
36966         
36967         var settings=this;
36968         ['xs','sm','md','lg'].map(function(size){
36969             if (settings[size]) {
36970                 cfg.cls += ' col-' + size + '-' + settings[size];
36971             }
36972         });
36973         
36974         return cfg;
36975         
36976     },
36977
36978     initEvents : function()
36979     {
36980         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36981         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36982         
36983         if(!this.fieldLabel.length){
36984             this.labelEl.hide();
36985         }
36986         
36987         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36988         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36989         
36990         this.indicator = this.indicatorEl();
36991         
36992         if(this.indicator){
36993             this.indicator.addClass('invisible');
36994         }
36995         
36996         this.originalValue = this.getValue();
36997         
36998     },
36999     
37000     inputEl: function ()
37001     {
37002         return this.el.select('.roo-radio-set-input', true).first();
37003     },
37004     
37005     getChildContainer : function()
37006     {
37007         return this.itemsEl;
37008     },
37009     
37010     register : function(item)
37011     {
37012         this.radioes.push(item);
37013         
37014     },
37015     
37016     validate : function()
37017     {   
37018         if(this.getVisibilityEl().hasClass('hidden')){
37019             return true;
37020         }
37021         
37022         var valid = false;
37023         
37024         Roo.each(this.radioes, function(i){
37025             if(!i.checked){
37026                 return;
37027             }
37028             
37029             valid = true;
37030             return false;
37031         });
37032         
37033         if(this.allowBlank) {
37034             return true;
37035         }
37036         
37037         if(this.disabled || valid){
37038             this.markValid();
37039             return true;
37040         }
37041         
37042         this.markInvalid();
37043         return false;
37044         
37045     },
37046     
37047     markValid : function()
37048     {
37049         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37050             this.indicatorEl().removeClass('visible');
37051             this.indicatorEl().addClass('invisible');
37052         }
37053         
37054         
37055         if (Roo.bootstrap.version == 3) {
37056             this.el.removeClass([this.invalidClass, this.validClass]);
37057             this.el.addClass(this.validClass);
37058         } else {
37059             this.el.removeClass(['is-invalid','is-valid']);
37060             this.el.addClass(['is-valid']);
37061         }
37062         this.fireEvent('valid', this);
37063     },
37064     
37065     markInvalid : function(msg)
37066     {
37067         if(this.allowBlank || this.disabled){
37068             return;
37069         }
37070         
37071         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37072             this.indicatorEl().removeClass('invisible');
37073             this.indicatorEl().addClass('visible');
37074         }
37075         if (Roo.bootstrap.version == 3) {
37076             this.el.removeClass([this.invalidClass, this.validClass]);
37077             this.el.addClass(this.invalidClass);
37078         } else {
37079             this.el.removeClass(['is-invalid','is-valid']);
37080             this.el.addClass(['is-invalid']);
37081         }
37082         
37083         this.fireEvent('invalid', this, msg);
37084         
37085     },
37086     
37087     setValue : function(v, suppressEvent)
37088     {   
37089         if(this.value === v){
37090             return;
37091         }
37092         
37093         this.value = v;
37094         
37095         if(this.rendered){
37096             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37097         }
37098         
37099         Roo.each(this.radioes, function(i){
37100             i.checked = false;
37101             i.el.removeClass('checked');
37102         });
37103         
37104         Roo.each(this.radioes, function(i){
37105             
37106             if(i.value === v || i.value.toString() === v.toString()){
37107                 i.checked = true;
37108                 i.el.addClass('checked');
37109                 
37110                 if(suppressEvent !== true){
37111                     this.fireEvent('check', this, i);
37112                 }
37113                 
37114                 return false;
37115             }
37116             
37117         }, this);
37118         
37119         this.validate();
37120     },
37121     
37122     clearInvalid : function(){
37123         
37124         if(!this.el || this.preventMark){
37125             return;
37126         }
37127         
37128         this.el.removeClass([this.invalidClass]);
37129         
37130         this.fireEvent('valid', this);
37131     }
37132     
37133 });
37134
37135 Roo.apply(Roo.bootstrap.RadioSet, {
37136     
37137     groups: {},
37138     
37139     register : function(set)
37140     {
37141         this.groups[set.name] = set;
37142     },
37143     
37144     get: function(name) 
37145     {
37146         if (typeof(this.groups[name]) == 'undefined') {
37147             return false;
37148         }
37149         
37150         return this.groups[name] ;
37151     }
37152     
37153 });
37154 /*
37155  * Based on:
37156  * Ext JS Library 1.1.1
37157  * Copyright(c) 2006-2007, Ext JS, LLC.
37158  *
37159  * Originally Released Under LGPL - original licence link has changed is not relivant.
37160  *
37161  * Fork - LGPL
37162  * <script type="text/javascript">
37163  */
37164
37165
37166 /**
37167  * @class Roo.bootstrap.SplitBar
37168  * @extends Roo.util.Observable
37169  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37170  * <br><br>
37171  * Usage:
37172  * <pre><code>
37173 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37174                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37175 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37176 split.minSize = 100;
37177 split.maxSize = 600;
37178 split.animate = true;
37179 split.on('moved', splitterMoved);
37180 </code></pre>
37181  * @constructor
37182  * Create a new SplitBar
37183  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37184  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37185  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37186  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37187                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37188                         position of the SplitBar).
37189  */
37190 Roo.bootstrap.SplitBar = function(cfg){
37191     
37192     /** @private */
37193     
37194     //{
37195     //  dragElement : elm
37196     //  resizingElement: el,
37197         // optional..
37198     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37199     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37200         // existingProxy ???
37201     //}
37202     
37203     this.el = Roo.get(cfg.dragElement, true);
37204     this.el.dom.unselectable = "on";
37205     /** @private */
37206     this.resizingEl = Roo.get(cfg.resizingElement, true);
37207
37208     /**
37209      * @private
37210      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37211      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37212      * @type Number
37213      */
37214     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37215     
37216     /**
37217      * The minimum size of the resizing element. (Defaults to 0)
37218      * @type Number
37219      */
37220     this.minSize = 0;
37221     
37222     /**
37223      * The maximum size of the resizing element. (Defaults to 2000)
37224      * @type Number
37225      */
37226     this.maxSize = 2000;
37227     
37228     /**
37229      * Whether to animate the transition to the new size
37230      * @type Boolean
37231      */
37232     this.animate = false;
37233     
37234     /**
37235      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37236      * @type Boolean
37237      */
37238     this.useShim = false;
37239     
37240     /** @private */
37241     this.shim = null;
37242     
37243     if(!cfg.existingProxy){
37244         /** @private */
37245         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37246     }else{
37247         this.proxy = Roo.get(cfg.existingProxy).dom;
37248     }
37249     /** @private */
37250     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37251     
37252     /** @private */
37253     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37254     
37255     /** @private */
37256     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37257     
37258     /** @private */
37259     this.dragSpecs = {};
37260     
37261     /**
37262      * @private The adapter to use to positon and resize elements
37263      */
37264     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37265     this.adapter.init(this);
37266     
37267     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37268         /** @private */
37269         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37270         this.el.addClass("roo-splitbar-h");
37271     }else{
37272         /** @private */
37273         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37274         this.el.addClass("roo-splitbar-v");
37275     }
37276     
37277     this.addEvents({
37278         /**
37279          * @event resize
37280          * Fires when the splitter is moved (alias for {@link #event-moved})
37281          * @param {Roo.bootstrap.SplitBar} this
37282          * @param {Number} newSize the new width or height
37283          */
37284         "resize" : true,
37285         /**
37286          * @event moved
37287          * Fires when the splitter is moved
37288          * @param {Roo.bootstrap.SplitBar} this
37289          * @param {Number} newSize the new width or height
37290          */
37291         "moved" : true,
37292         /**
37293          * @event beforeresize
37294          * Fires before the splitter is dragged
37295          * @param {Roo.bootstrap.SplitBar} this
37296          */
37297         "beforeresize" : true,
37298
37299         "beforeapply" : true
37300     });
37301
37302     Roo.util.Observable.call(this);
37303 };
37304
37305 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37306     onStartProxyDrag : function(x, y){
37307         this.fireEvent("beforeresize", this);
37308         if(!this.overlay){
37309             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37310             o.unselectable();
37311             o.enableDisplayMode("block");
37312             // all splitbars share the same overlay
37313             Roo.bootstrap.SplitBar.prototype.overlay = o;
37314         }
37315         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37316         this.overlay.show();
37317         Roo.get(this.proxy).setDisplayed("block");
37318         var size = this.adapter.getElementSize(this);
37319         this.activeMinSize = this.getMinimumSize();;
37320         this.activeMaxSize = this.getMaximumSize();;
37321         var c1 = size - this.activeMinSize;
37322         var c2 = Math.max(this.activeMaxSize - size, 0);
37323         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37324             this.dd.resetConstraints();
37325             this.dd.setXConstraint(
37326                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37327                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37328             );
37329             this.dd.setYConstraint(0, 0);
37330         }else{
37331             this.dd.resetConstraints();
37332             this.dd.setXConstraint(0, 0);
37333             this.dd.setYConstraint(
37334                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37335                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37336             );
37337          }
37338         this.dragSpecs.startSize = size;
37339         this.dragSpecs.startPoint = [x, y];
37340         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37341     },
37342     
37343     /** 
37344      * @private Called after the drag operation by the DDProxy
37345      */
37346     onEndProxyDrag : function(e){
37347         Roo.get(this.proxy).setDisplayed(false);
37348         var endPoint = Roo.lib.Event.getXY(e);
37349         if(this.overlay){
37350             this.overlay.hide();
37351         }
37352         var newSize;
37353         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37354             newSize = this.dragSpecs.startSize + 
37355                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37356                     endPoint[0] - this.dragSpecs.startPoint[0] :
37357                     this.dragSpecs.startPoint[0] - endPoint[0]
37358                 );
37359         }else{
37360             newSize = this.dragSpecs.startSize + 
37361                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37362                     endPoint[1] - this.dragSpecs.startPoint[1] :
37363                     this.dragSpecs.startPoint[1] - endPoint[1]
37364                 );
37365         }
37366         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37367         if(newSize != this.dragSpecs.startSize){
37368             if(this.fireEvent('beforeapply', this, newSize) !== false){
37369                 this.adapter.setElementSize(this, newSize);
37370                 this.fireEvent("moved", this, newSize);
37371                 this.fireEvent("resize", this, newSize);
37372             }
37373         }
37374     },
37375     
37376     /**
37377      * Get the adapter this SplitBar uses
37378      * @return The adapter object
37379      */
37380     getAdapter : function(){
37381         return this.adapter;
37382     },
37383     
37384     /**
37385      * Set the adapter this SplitBar uses
37386      * @param {Object} adapter A SplitBar adapter object
37387      */
37388     setAdapter : function(adapter){
37389         this.adapter = adapter;
37390         this.adapter.init(this);
37391     },
37392     
37393     /**
37394      * Gets the minimum size for the resizing element
37395      * @return {Number} The minimum size
37396      */
37397     getMinimumSize : function(){
37398         return this.minSize;
37399     },
37400     
37401     /**
37402      * Sets the minimum size for the resizing element
37403      * @param {Number} minSize The minimum size
37404      */
37405     setMinimumSize : function(minSize){
37406         this.minSize = minSize;
37407     },
37408     
37409     /**
37410      * Gets the maximum size for the resizing element
37411      * @return {Number} The maximum size
37412      */
37413     getMaximumSize : function(){
37414         return this.maxSize;
37415     },
37416     
37417     /**
37418      * Sets the maximum size for the resizing element
37419      * @param {Number} maxSize The maximum size
37420      */
37421     setMaximumSize : function(maxSize){
37422         this.maxSize = maxSize;
37423     },
37424     
37425     /**
37426      * Sets the initialize size for the resizing element
37427      * @param {Number} size The initial size
37428      */
37429     setCurrentSize : function(size){
37430         var oldAnimate = this.animate;
37431         this.animate = false;
37432         this.adapter.setElementSize(this, size);
37433         this.animate = oldAnimate;
37434     },
37435     
37436     /**
37437      * Destroy this splitbar. 
37438      * @param {Boolean} removeEl True to remove the element
37439      */
37440     destroy : function(removeEl){
37441         if(this.shim){
37442             this.shim.remove();
37443         }
37444         this.dd.unreg();
37445         this.proxy.parentNode.removeChild(this.proxy);
37446         if(removeEl){
37447             this.el.remove();
37448         }
37449     }
37450 });
37451
37452 /**
37453  * @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.
37454  */
37455 Roo.bootstrap.SplitBar.createProxy = function(dir){
37456     var proxy = new Roo.Element(document.createElement("div"));
37457     proxy.unselectable();
37458     var cls = 'roo-splitbar-proxy';
37459     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37460     document.body.appendChild(proxy.dom);
37461     return proxy.dom;
37462 };
37463
37464 /** 
37465  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37466  * Default Adapter. It assumes the splitter and resizing element are not positioned
37467  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37468  */
37469 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37470 };
37471
37472 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37473     // do nothing for now
37474     init : function(s){
37475     
37476     },
37477     /**
37478      * Called before drag operations to get the current size of the resizing element. 
37479      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37480      */
37481      getElementSize : function(s){
37482         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37483             return s.resizingEl.getWidth();
37484         }else{
37485             return s.resizingEl.getHeight();
37486         }
37487     },
37488     
37489     /**
37490      * Called after drag operations to set the size of the resizing element.
37491      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37492      * @param {Number} newSize The new size to set
37493      * @param {Function} onComplete A function to be invoked when resizing is complete
37494      */
37495     setElementSize : function(s, newSize, onComplete){
37496         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37497             if(!s.animate){
37498                 s.resizingEl.setWidth(newSize);
37499                 if(onComplete){
37500                     onComplete(s, newSize);
37501                 }
37502             }else{
37503                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37504             }
37505         }else{
37506             
37507             if(!s.animate){
37508                 s.resizingEl.setHeight(newSize);
37509                 if(onComplete){
37510                     onComplete(s, newSize);
37511                 }
37512             }else{
37513                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37514             }
37515         }
37516     }
37517 };
37518
37519 /** 
37520  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37521  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37522  * Adapter that  moves the splitter element to align with the resized sizing element. 
37523  * Used with an absolute positioned SplitBar.
37524  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37525  * document.body, make sure you assign an id to the body element.
37526  */
37527 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37528     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37529     this.container = Roo.get(container);
37530 };
37531
37532 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37533     init : function(s){
37534         this.basic.init(s);
37535     },
37536     
37537     getElementSize : function(s){
37538         return this.basic.getElementSize(s);
37539     },
37540     
37541     setElementSize : function(s, newSize, onComplete){
37542         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37543     },
37544     
37545     moveSplitter : function(s){
37546         var yes = Roo.bootstrap.SplitBar;
37547         switch(s.placement){
37548             case yes.LEFT:
37549                 s.el.setX(s.resizingEl.getRight());
37550                 break;
37551             case yes.RIGHT:
37552                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37553                 break;
37554             case yes.TOP:
37555                 s.el.setY(s.resizingEl.getBottom());
37556                 break;
37557             case yes.BOTTOM:
37558                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37559                 break;
37560         }
37561     }
37562 };
37563
37564 /**
37565  * Orientation constant - Create a vertical SplitBar
37566  * @static
37567  * @type Number
37568  */
37569 Roo.bootstrap.SplitBar.VERTICAL = 1;
37570
37571 /**
37572  * Orientation constant - Create a horizontal SplitBar
37573  * @static
37574  * @type Number
37575  */
37576 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37577
37578 /**
37579  * Placement constant - The resizing element is to the left of the splitter element
37580  * @static
37581  * @type Number
37582  */
37583 Roo.bootstrap.SplitBar.LEFT = 1;
37584
37585 /**
37586  * Placement constant - The resizing element is to the right of the splitter element
37587  * @static
37588  * @type Number
37589  */
37590 Roo.bootstrap.SplitBar.RIGHT = 2;
37591
37592 /**
37593  * Placement constant - The resizing element is positioned above the splitter element
37594  * @static
37595  * @type Number
37596  */
37597 Roo.bootstrap.SplitBar.TOP = 3;
37598
37599 /**
37600  * Placement constant - The resizing element is positioned under splitter element
37601  * @static
37602  * @type Number
37603  */
37604 Roo.bootstrap.SplitBar.BOTTOM = 4;
37605 Roo.namespace("Roo.bootstrap.layout");/*
37606  * Based on:
37607  * Ext JS Library 1.1.1
37608  * Copyright(c) 2006-2007, Ext JS, LLC.
37609  *
37610  * Originally Released Under LGPL - original licence link has changed is not relivant.
37611  *
37612  * Fork - LGPL
37613  * <script type="text/javascript">
37614  */
37615
37616 /**
37617  * @class Roo.bootstrap.layout.Manager
37618  * @extends Roo.bootstrap.Component
37619  * Base class for layout managers.
37620  */
37621 Roo.bootstrap.layout.Manager = function(config)
37622 {
37623     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37624
37625
37626
37627
37628
37629     /** false to disable window resize monitoring @type Boolean */
37630     this.monitorWindowResize = true;
37631     this.regions = {};
37632     this.addEvents({
37633         /**
37634          * @event layout
37635          * Fires when a layout is performed.
37636          * @param {Roo.LayoutManager} this
37637          */
37638         "layout" : true,
37639         /**
37640          * @event regionresized
37641          * Fires when the user resizes a region.
37642          * @param {Roo.LayoutRegion} region The resized region
37643          * @param {Number} newSize The new size (width for east/west, height for north/south)
37644          */
37645         "regionresized" : true,
37646         /**
37647          * @event regioncollapsed
37648          * Fires when a region is collapsed.
37649          * @param {Roo.LayoutRegion} region The collapsed region
37650          */
37651         "regioncollapsed" : true,
37652         /**
37653          * @event regionexpanded
37654          * Fires when a region is expanded.
37655          * @param {Roo.LayoutRegion} region The expanded region
37656          */
37657         "regionexpanded" : true
37658     });
37659     this.updating = false;
37660
37661     if (config.el) {
37662         this.el = Roo.get(config.el);
37663         this.initEvents();
37664     }
37665
37666 };
37667
37668 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37669
37670
37671     regions : null,
37672
37673     monitorWindowResize : true,
37674
37675
37676     updating : false,
37677
37678
37679     onRender : function(ct, position)
37680     {
37681         if(!this.el){
37682             this.el = Roo.get(ct);
37683             this.initEvents();
37684         }
37685         //this.fireEvent('render',this);
37686     },
37687
37688
37689     initEvents: function()
37690     {
37691
37692
37693         // ie scrollbar fix
37694         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37695             document.body.scroll = "no";
37696         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37697             this.el.position('relative');
37698         }
37699         this.id = this.el.id;
37700         this.el.addClass("roo-layout-container");
37701         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37702         if(this.el.dom != document.body ) {
37703             this.el.on('resize', this.layout,this);
37704             this.el.on('show', this.layout,this);
37705         }
37706
37707     },
37708
37709     /**
37710      * Returns true if this layout is currently being updated
37711      * @return {Boolean}
37712      */
37713     isUpdating : function(){
37714         return this.updating;
37715     },
37716
37717     /**
37718      * Suspend the LayoutManager from doing auto-layouts while
37719      * making multiple add or remove calls
37720      */
37721     beginUpdate : function(){
37722         this.updating = true;
37723     },
37724
37725     /**
37726      * Restore auto-layouts and optionally disable the manager from performing a layout
37727      * @param {Boolean} noLayout true to disable a layout update
37728      */
37729     endUpdate : function(noLayout){
37730         this.updating = false;
37731         if(!noLayout){
37732             this.layout();
37733         }
37734     },
37735
37736     layout: function(){
37737         // abstract...
37738     },
37739
37740     onRegionResized : function(region, newSize){
37741         this.fireEvent("regionresized", region, newSize);
37742         this.layout();
37743     },
37744
37745     onRegionCollapsed : function(region){
37746         this.fireEvent("regioncollapsed", region);
37747     },
37748
37749     onRegionExpanded : function(region){
37750         this.fireEvent("regionexpanded", region);
37751     },
37752
37753     /**
37754      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37755      * performs box-model adjustments.
37756      * @return {Object} The size as an object {width: (the width), height: (the height)}
37757      */
37758     getViewSize : function()
37759     {
37760         var size;
37761         if(this.el.dom != document.body){
37762             size = this.el.getSize();
37763         }else{
37764             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37765         }
37766         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37767         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37768         return size;
37769     },
37770
37771     /**
37772      * Returns the Element this layout is bound to.
37773      * @return {Roo.Element}
37774      */
37775     getEl : function(){
37776         return this.el;
37777     },
37778
37779     /**
37780      * Returns the specified region.
37781      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37782      * @return {Roo.LayoutRegion}
37783      */
37784     getRegion : function(target){
37785         return this.regions[target.toLowerCase()];
37786     },
37787
37788     onWindowResize : function(){
37789         if(this.monitorWindowResize){
37790             this.layout();
37791         }
37792     }
37793 });
37794 /*
37795  * Based on:
37796  * Ext JS Library 1.1.1
37797  * Copyright(c) 2006-2007, Ext JS, LLC.
37798  *
37799  * Originally Released Under LGPL - original licence link has changed is not relivant.
37800  *
37801  * Fork - LGPL
37802  * <script type="text/javascript">
37803  */
37804 /**
37805  * @class Roo.bootstrap.layout.Border
37806  * @extends Roo.bootstrap.layout.Manager
37807  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37808  * please see: examples/bootstrap/nested.html<br><br>
37809  
37810 <b>The container the layout is rendered into can be either the body element or any other element.
37811 If it is not the body element, the container needs to either be an absolute positioned element,
37812 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37813 the container size if it is not the body element.</b>
37814
37815 * @constructor
37816 * Create a new Border
37817 * @param {Object} config Configuration options
37818  */
37819 Roo.bootstrap.layout.Border = function(config){
37820     config = config || {};
37821     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37822     
37823     
37824     
37825     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37826         if(config[region]){
37827             config[region].region = region;
37828             this.addRegion(config[region]);
37829         }
37830     },this);
37831     
37832 };
37833
37834 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37835
37836 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37837     
37838     parent : false, // this might point to a 'nest' or a ???
37839     
37840     /**
37841      * Creates and adds a new region if it doesn't already exist.
37842      * @param {String} target The target region key (north, south, east, west or center).
37843      * @param {Object} config The regions config object
37844      * @return {BorderLayoutRegion} The new region
37845      */
37846     addRegion : function(config)
37847     {
37848         if(!this.regions[config.region]){
37849             var r = this.factory(config);
37850             this.bindRegion(r);
37851         }
37852         return this.regions[config.region];
37853     },
37854
37855     // private (kinda)
37856     bindRegion : function(r){
37857         this.regions[r.config.region] = r;
37858         
37859         r.on("visibilitychange",    this.layout, this);
37860         r.on("paneladded",          this.layout, this);
37861         r.on("panelremoved",        this.layout, this);
37862         r.on("invalidated",         this.layout, this);
37863         r.on("resized",             this.onRegionResized, this);
37864         r.on("collapsed",           this.onRegionCollapsed, this);
37865         r.on("expanded",            this.onRegionExpanded, this);
37866     },
37867
37868     /**
37869      * Performs a layout update.
37870      */
37871     layout : function()
37872     {
37873         if(this.updating) {
37874             return;
37875         }
37876         
37877         // render all the rebions if they have not been done alreayd?
37878         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37879             if(this.regions[region] && !this.regions[region].bodyEl){
37880                 this.regions[region].onRender(this.el)
37881             }
37882         },this);
37883         
37884         var size = this.getViewSize();
37885         var w = size.width;
37886         var h = size.height;
37887         var centerW = w;
37888         var centerH = h;
37889         var centerY = 0;
37890         var centerX = 0;
37891         //var x = 0, y = 0;
37892
37893         var rs = this.regions;
37894         var north = rs["north"];
37895         var south = rs["south"]; 
37896         var west = rs["west"];
37897         var east = rs["east"];
37898         var center = rs["center"];
37899         //if(this.hideOnLayout){ // not supported anymore
37900             //c.el.setStyle("display", "none");
37901         //}
37902         if(north && north.isVisible()){
37903             var b = north.getBox();
37904             var m = north.getMargins();
37905             b.width = w - (m.left+m.right);
37906             b.x = m.left;
37907             b.y = m.top;
37908             centerY = b.height + b.y + m.bottom;
37909             centerH -= centerY;
37910             north.updateBox(this.safeBox(b));
37911         }
37912         if(south && south.isVisible()){
37913             var b = south.getBox();
37914             var m = south.getMargins();
37915             b.width = w - (m.left+m.right);
37916             b.x = m.left;
37917             var totalHeight = (b.height + m.top + m.bottom);
37918             b.y = h - totalHeight + m.top;
37919             centerH -= totalHeight;
37920             south.updateBox(this.safeBox(b));
37921         }
37922         if(west && west.isVisible()){
37923             var b = west.getBox();
37924             var m = west.getMargins();
37925             b.height = centerH - (m.top+m.bottom);
37926             b.x = m.left;
37927             b.y = centerY + m.top;
37928             var totalWidth = (b.width + m.left + m.right);
37929             centerX += totalWidth;
37930             centerW -= totalWidth;
37931             west.updateBox(this.safeBox(b));
37932         }
37933         if(east && east.isVisible()){
37934             var b = east.getBox();
37935             var m = east.getMargins();
37936             b.height = centerH - (m.top+m.bottom);
37937             var totalWidth = (b.width + m.left + m.right);
37938             b.x = w - totalWidth + m.left;
37939             b.y = centerY + m.top;
37940             centerW -= totalWidth;
37941             east.updateBox(this.safeBox(b));
37942         }
37943         if(center){
37944             var m = center.getMargins();
37945             var centerBox = {
37946                 x: centerX + m.left,
37947                 y: centerY + m.top,
37948                 width: centerW - (m.left+m.right),
37949                 height: centerH - (m.top+m.bottom)
37950             };
37951             //if(this.hideOnLayout){
37952                 //center.el.setStyle("display", "block");
37953             //}
37954             center.updateBox(this.safeBox(centerBox));
37955         }
37956         this.el.repaint();
37957         this.fireEvent("layout", this);
37958     },
37959
37960     // private
37961     safeBox : function(box){
37962         box.width = Math.max(0, box.width);
37963         box.height = Math.max(0, box.height);
37964         return box;
37965     },
37966
37967     /**
37968      * Adds a ContentPanel (or subclass) to this layout.
37969      * @param {String} target The target region key (north, south, east, west or center).
37970      * @param {Roo.ContentPanel} panel The panel to add
37971      * @return {Roo.ContentPanel} The added panel
37972      */
37973     add : function(target, panel){
37974          
37975         target = target.toLowerCase();
37976         return this.regions[target].add(panel);
37977     },
37978
37979     /**
37980      * Remove a ContentPanel (or subclass) to this layout.
37981      * @param {String} target The target region key (north, south, east, west or center).
37982      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37983      * @return {Roo.ContentPanel} The removed panel
37984      */
37985     remove : function(target, panel){
37986         target = target.toLowerCase();
37987         return this.regions[target].remove(panel);
37988     },
37989
37990     /**
37991      * Searches all regions for a panel with the specified id
37992      * @param {String} panelId
37993      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37994      */
37995     findPanel : function(panelId){
37996         var rs = this.regions;
37997         for(var target in rs){
37998             if(typeof rs[target] != "function"){
37999                 var p = rs[target].getPanel(panelId);
38000                 if(p){
38001                     return p;
38002                 }
38003             }
38004         }
38005         return null;
38006     },
38007
38008     /**
38009      * Searches all regions for a panel with the specified id and activates (shows) it.
38010      * @param {String/ContentPanel} panelId The panels id or the panel itself
38011      * @return {Roo.ContentPanel} The shown panel or null
38012      */
38013     showPanel : function(panelId) {
38014       var rs = this.regions;
38015       for(var target in rs){
38016          var r = rs[target];
38017          if(typeof r != "function"){
38018             if(r.hasPanel(panelId)){
38019                return r.showPanel(panelId);
38020             }
38021          }
38022       }
38023       return null;
38024    },
38025
38026    /**
38027      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38028      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38029      */
38030    /*
38031     restoreState : function(provider){
38032         if(!provider){
38033             provider = Roo.state.Manager;
38034         }
38035         var sm = new Roo.LayoutStateManager();
38036         sm.init(this, provider);
38037     },
38038 */
38039  
38040  
38041     /**
38042      * Adds a xtype elements to the layout.
38043      * <pre><code>
38044
38045 layout.addxtype({
38046        xtype : 'ContentPanel',
38047        region: 'west',
38048        items: [ .... ]
38049    }
38050 );
38051
38052 layout.addxtype({
38053         xtype : 'NestedLayoutPanel',
38054         region: 'west',
38055         layout: {
38056            center: { },
38057            west: { }   
38058         },
38059         items : [ ... list of content panels or nested layout panels.. ]
38060    }
38061 );
38062 </code></pre>
38063      * @param {Object} cfg Xtype definition of item to add.
38064      */
38065     addxtype : function(cfg)
38066     {
38067         // basically accepts a pannel...
38068         // can accept a layout region..!?!?
38069         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38070         
38071         
38072         // theory?  children can only be panels??
38073         
38074         //if (!cfg.xtype.match(/Panel$/)) {
38075         //    return false;
38076         //}
38077         var ret = false;
38078         
38079         if (typeof(cfg.region) == 'undefined') {
38080             Roo.log("Failed to add Panel, region was not set");
38081             Roo.log(cfg);
38082             return false;
38083         }
38084         var region = cfg.region;
38085         delete cfg.region;
38086         
38087           
38088         var xitems = [];
38089         if (cfg.items) {
38090             xitems = cfg.items;
38091             delete cfg.items;
38092         }
38093         var nb = false;
38094         
38095         if ( region == 'center') {
38096             Roo.log("Center: " + cfg.title);
38097         }
38098         
38099         
38100         switch(cfg.xtype) 
38101         {
38102             case 'Content':  // ContentPanel (el, cfg)
38103             case 'Scroll':  // ContentPanel (el, cfg)
38104             case 'View': 
38105                 cfg.autoCreate = cfg.autoCreate || true;
38106                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38107                 //} else {
38108                 //    var el = this.el.createChild();
38109                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38110                 //}
38111                 
38112                 this.add(region, ret);
38113                 break;
38114             
38115             /*
38116             case 'TreePanel': // our new panel!
38117                 cfg.el = this.el.createChild();
38118                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38119                 this.add(region, ret);
38120                 break;
38121             */
38122             
38123             case 'Nest': 
38124                 // create a new Layout (which is  a Border Layout...
38125                 
38126                 var clayout = cfg.layout;
38127                 clayout.el  = this.el.createChild();
38128                 clayout.items   = clayout.items  || [];
38129                 
38130                 delete cfg.layout;
38131                 
38132                 // replace this exitems with the clayout ones..
38133                 xitems = clayout.items;
38134                  
38135                 // force background off if it's in center...
38136                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38137                     cfg.background = false;
38138                 }
38139                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38140                 
38141                 
38142                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38143                 //console.log('adding nested layout panel '  + cfg.toSource());
38144                 this.add(region, ret);
38145                 nb = {}; /// find first...
38146                 break;
38147             
38148             case 'Grid':
38149                 
38150                 // needs grid and region
38151                 
38152                 //var el = this.getRegion(region).el.createChild();
38153                 /*
38154                  *var el = this.el.createChild();
38155                 // create the grid first...
38156                 cfg.grid.container = el;
38157                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38158                 */
38159                 
38160                 if (region == 'center' && this.active ) {
38161                     cfg.background = false;
38162                 }
38163                 
38164                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38165                 
38166                 this.add(region, ret);
38167                 /*
38168                 if (cfg.background) {
38169                     // render grid on panel activation (if panel background)
38170                     ret.on('activate', function(gp) {
38171                         if (!gp.grid.rendered) {
38172                     //        gp.grid.render(el);
38173                         }
38174                     });
38175                 } else {
38176                   //  cfg.grid.render(el);
38177                 }
38178                 */
38179                 break;
38180            
38181            
38182             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38183                 // it was the old xcomponent building that caused this before.
38184                 // espeically if border is the top element in the tree.
38185                 ret = this;
38186                 break; 
38187                 
38188                     
38189                 
38190                 
38191                 
38192             default:
38193                 /*
38194                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38195                     
38196                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38197                     this.add(region, ret);
38198                 } else {
38199                 */
38200                     Roo.log(cfg);
38201                     throw "Can not add '" + cfg.xtype + "' to Border";
38202                     return null;
38203              
38204                                 
38205              
38206         }
38207         this.beginUpdate();
38208         // add children..
38209         var region = '';
38210         var abn = {};
38211         Roo.each(xitems, function(i)  {
38212             region = nb && i.region ? i.region : false;
38213             
38214             var add = ret.addxtype(i);
38215            
38216             if (region) {
38217                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38218                 if (!i.background) {
38219                     abn[region] = nb[region] ;
38220                 }
38221             }
38222             
38223         });
38224         this.endUpdate();
38225
38226         // make the last non-background panel active..
38227         //if (nb) { Roo.log(abn); }
38228         if (nb) {
38229             
38230             for(var r in abn) {
38231                 region = this.getRegion(r);
38232                 if (region) {
38233                     // tried using nb[r], but it does not work..
38234                      
38235                     region.showPanel(abn[r]);
38236                    
38237                 }
38238             }
38239         }
38240         return ret;
38241         
38242     },
38243     
38244     
38245 // private
38246     factory : function(cfg)
38247     {
38248         
38249         var validRegions = Roo.bootstrap.layout.Border.regions;
38250
38251         var target = cfg.region;
38252         cfg.mgr = this;
38253         
38254         var r = Roo.bootstrap.layout;
38255         Roo.log(target);
38256         switch(target){
38257             case "north":
38258                 return new r.North(cfg);
38259             case "south":
38260                 return new r.South(cfg);
38261             case "east":
38262                 return new r.East(cfg);
38263             case "west":
38264                 return new r.West(cfg);
38265             case "center":
38266                 return new r.Center(cfg);
38267         }
38268         throw 'Layout region "'+target+'" not supported.';
38269     }
38270     
38271     
38272 });
38273  /*
38274  * Based on:
38275  * Ext JS Library 1.1.1
38276  * Copyright(c) 2006-2007, Ext JS, LLC.
38277  *
38278  * Originally Released Under LGPL - original licence link has changed is not relivant.
38279  *
38280  * Fork - LGPL
38281  * <script type="text/javascript">
38282  */
38283  
38284 /**
38285  * @class Roo.bootstrap.layout.Basic
38286  * @extends Roo.util.Observable
38287  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38288  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38289  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38290  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38291  * @cfg {string}   region  the region that it inhabits..
38292  * @cfg {bool}   skipConfig skip config?
38293  * 
38294
38295  */
38296 Roo.bootstrap.layout.Basic = function(config){
38297     
38298     this.mgr = config.mgr;
38299     
38300     this.position = config.region;
38301     
38302     var skipConfig = config.skipConfig;
38303     
38304     this.events = {
38305         /**
38306          * @scope Roo.BasicLayoutRegion
38307          */
38308         
38309         /**
38310          * @event beforeremove
38311          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38312          * @param {Roo.LayoutRegion} this
38313          * @param {Roo.ContentPanel} panel The panel
38314          * @param {Object} e The cancel event object
38315          */
38316         "beforeremove" : true,
38317         /**
38318          * @event invalidated
38319          * Fires when the layout for this region is changed.
38320          * @param {Roo.LayoutRegion} this
38321          */
38322         "invalidated" : true,
38323         /**
38324          * @event visibilitychange
38325          * Fires when this region is shown or hidden 
38326          * @param {Roo.LayoutRegion} this
38327          * @param {Boolean} visibility true or false
38328          */
38329         "visibilitychange" : true,
38330         /**
38331          * @event paneladded
38332          * Fires when a panel is added. 
38333          * @param {Roo.LayoutRegion} this
38334          * @param {Roo.ContentPanel} panel The panel
38335          */
38336         "paneladded" : true,
38337         /**
38338          * @event panelremoved
38339          * Fires when a panel is removed. 
38340          * @param {Roo.LayoutRegion} this
38341          * @param {Roo.ContentPanel} panel The panel
38342          */
38343         "panelremoved" : true,
38344         /**
38345          * @event beforecollapse
38346          * Fires when this region before collapse.
38347          * @param {Roo.LayoutRegion} this
38348          */
38349         "beforecollapse" : true,
38350         /**
38351          * @event collapsed
38352          * Fires when this region is collapsed.
38353          * @param {Roo.LayoutRegion} this
38354          */
38355         "collapsed" : true,
38356         /**
38357          * @event expanded
38358          * Fires when this region is expanded.
38359          * @param {Roo.LayoutRegion} this
38360          */
38361         "expanded" : true,
38362         /**
38363          * @event slideshow
38364          * Fires when this region is slid into view.
38365          * @param {Roo.LayoutRegion} this
38366          */
38367         "slideshow" : true,
38368         /**
38369          * @event slidehide
38370          * Fires when this region slides out of view. 
38371          * @param {Roo.LayoutRegion} this
38372          */
38373         "slidehide" : true,
38374         /**
38375          * @event panelactivated
38376          * Fires when a panel is activated. 
38377          * @param {Roo.LayoutRegion} this
38378          * @param {Roo.ContentPanel} panel The activated panel
38379          */
38380         "panelactivated" : true,
38381         /**
38382          * @event resized
38383          * Fires when the user resizes this region. 
38384          * @param {Roo.LayoutRegion} this
38385          * @param {Number} newSize The new size (width for east/west, height for north/south)
38386          */
38387         "resized" : true
38388     };
38389     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38390     this.panels = new Roo.util.MixedCollection();
38391     this.panels.getKey = this.getPanelId.createDelegate(this);
38392     this.box = null;
38393     this.activePanel = null;
38394     // ensure listeners are added...
38395     
38396     if (config.listeners || config.events) {
38397         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38398             listeners : config.listeners || {},
38399             events : config.events || {}
38400         });
38401     }
38402     
38403     if(skipConfig !== true){
38404         this.applyConfig(config);
38405     }
38406 };
38407
38408 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38409 {
38410     getPanelId : function(p){
38411         return p.getId();
38412     },
38413     
38414     applyConfig : function(config){
38415         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38416         this.config = config;
38417         
38418     },
38419     
38420     /**
38421      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38422      * the width, for horizontal (north, south) the height.
38423      * @param {Number} newSize The new width or height
38424      */
38425     resizeTo : function(newSize){
38426         var el = this.el ? this.el :
38427                  (this.activePanel ? this.activePanel.getEl() : null);
38428         if(el){
38429             switch(this.position){
38430                 case "east":
38431                 case "west":
38432                     el.setWidth(newSize);
38433                     this.fireEvent("resized", this, newSize);
38434                 break;
38435                 case "north":
38436                 case "south":
38437                     el.setHeight(newSize);
38438                     this.fireEvent("resized", this, newSize);
38439                 break;                
38440             }
38441         }
38442     },
38443     
38444     getBox : function(){
38445         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38446     },
38447     
38448     getMargins : function(){
38449         return this.margins;
38450     },
38451     
38452     updateBox : function(box){
38453         this.box = box;
38454         var el = this.activePanel.getEl();
38455         el.dom.style.left = box.x + "px";
38456         el.dom.style.top = box.y + "px";
38457         this.activePanel.setSize(box.width, box.height);
38458     },
38459     
38460     /**
38461      * Returns the container element for this region.
38462      * @return {Roo.Element}
38463      */
38464     getEl : function(){
38465         return this.activePanel;
38466     },
38467     
38468     /**
38469      * Returns true if this region is currently visible.
38470      * @return {Boolean}
38471      */
38472     isVisible : function(){
38473         return this.activePanel ? true : false;
38474     },
38475     
38476     setActivePanel : function(panel){
38477         panel = this.getPanel(panel);
38478         if(this.activePanel && this.activePanel != panel){
38479             this.activePanel.setActiveState(false);
38480             this.activePanel.getEl().setLeftTop(-10000,-10000);
38481         }
38482         this.activePanel = panel;
38483         panel.setActiveState(true);
38484         if(this.box){
38485             panel.setSize(this.box.width, this.box.height);
38486         }
38487         this.fireEvent("panelactivated", this, panel);
38488         this.fireEvent("invalidated");
38489     },
38490     
38491     /**
38492      * Show the specified panel.
38493      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38494      * @return {Roo.ContentPanel} The shown panel or null
38495      */
38496     showPanel : function(panel){
38497         panel = this.getPanel(panel);
38498         if(panel){
38499             this.setActivePanel(panel);
38500         }
38501         return panel;
38502     },
38503     
38504     /**
38505      * Get the active panel for this region.
38506      * @return {Roo.ContentPanel} The active panel or null
38507      */
38508     getActivePanel : function(){
38509         return this.activePanel;
38510     },
38511     
38512     /**
38513      * Add the passed ContentPanel(s)
38514      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38515      * @return {Roo.ContentPanel} The panel added (if only one was added)
38516      */
38517     add : function(panel){
38518         if(arguments.length > 1){
38519             for(var i = 0, len = arguments.length; i < len; i++) {
38520                 this.add(arguments[i]);
38521             }
38522             return null;
38523         }
38524         if(this.hasPanel(panel)){
38525             this.showPanel(panel);
38526             return panel;
38527         }
38528         var el = panel.getEl();
38529         if(el.dom.parentNode != this.mgr.el.dom){
38530             this.mgr.el.dom.appendChild(el.dom);
38531         }
38532         if(panel.setRegion){
38533             panel.setRegion(this);
38534         }
38535         this.panels.add(panel);
38536         el.setStyle("position", "absolute");
38537         if(!panel.background){
38538             this.setActivePanel(panel);
38539             if(this.config.initialSize && this.panels.getCount()==1){
38540                 this.resizeTo(this.config.initialSize);
38541             }
38542         }
38543         this.fireEvent("paneladded", this, panel);
38544         return panel;
38545     },
38546     
38547     /**
38548      * Returns true if the panel is in this region.
38549      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38550      * @return {Boolean}
38551      */
38552     hasPanel : function(panel){
38553         if(typeof panel == "object"){ // must be panel obj
38554             panel = panel.getId();
38555         }
38556         return this.getPanel(panel) ? true : false;
38557     },
38558     
38559     /**
38560      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38561      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38562      * @param {Boolean} preservePanel Overrides the config preservePanel option
38563      * @return {Roo.ContentPanel} The panel that was removed
38564      */
38565     remove : function(panel, preservePanel){
38566         panel = this.getPanel(panel);
38567         if(!panel){
38568             return null;
38569         }
38570         var e = {};
38571         this.fireEvent("beforeremove", this, panel, e);
38572         if(e.cancel === true){
38573             return null;
38574         }
38575         var panelId = panel.getId();
38576         this.panels.removeKey(panelId);
38577         return panel;
38578     },
38579     
38580     /**
38581      * Returns the panel specified or null if it's not in this region.
38582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38583      * @return {Roo.ContentPanel}
38584      */
38585     getPanel : function(id){
38586         if(typeof id == "object"){ // must be panel obj
38587             return id;
38588         }
38589         return this.panels.get(id);
38590     },
38591     
38592     /**
38593      * Returns this regions position (north/south/east/west/center).
38594      * @return {String} 
38595      */
38596     getPosition: function(){
38597         return this.position;    
38598     }
38599 });/*
38600  * Based on:
38601  * Ext JS Library 1.1.1
38602  * Copyright(c) 2006-2007, Ext JS, LLC.
38603  *
38604  * Originally Released Under LGPL - original licence link has changed is not relivant.
38605  *
38606  * Fork - LGPL
38607  * <script type="text/javascript">
38608  */
38609  
38610 /**
38611  * @class Roo.bootstrap.layout.Region
38612  * @extends Roo.bootstrap.layout.Basic
38613  * This class represents a region in a layout manager.
38614  
38615  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38616  * @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})
38617  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38618  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38619  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38620  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38621  * @cfg {String}    title           The title for the region (overrides panel titles)
38622  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38623  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38624  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38625  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38626  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38627  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38628  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38629  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38630  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38631  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38632
38633  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38634  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38635  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38636  * @cfg {Number}    width           For East/West panels
38637  * @cfg {Number}    height          For North/South panels
38638  * @cfg {Boolean}   split           To show the splitter
38639  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38640  * 
38641  * @cfg {string}   cls             Extra CSS classes to add to region
38642  * 
38643  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38644  * @cfg {string}   region  the region that it inhabits..
38645  *
38646
38647  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38648  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38649
38650  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38651  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38652  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38653  */
38654 Roo.bootstrap.layout.Region = function(config)
38655 {
38656     this.applyConfig(config);
38657
38658     var mgr = config.mgr;
38659     var pos = config.region;
38660     config.skipConfig = true;
38661     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38662     
38663     if (mgr.el) {
38664         this.onRender(mgr.el);   
38665     }
38666      
38667     this.visible = true;
38668     this.collapsed = false;
38669     this.unrendered_panels = [];
38670 };
38671
38672 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38673
38674     position: '', // set by wrapper (eg. north/south etc..)
38675     unrendered_panels : null,  // unrendered panels.
38676     
38677     tabPosition : false,
38678     
38679     mgr: false, // points to 'Border'
38680     
38681     
38682     createBody : function(){
38683         /** This region's body element 
38684         * @type Roo.Element */
38685         this.bodyEl = this.el.createChild({
38686                 tag: "div",
38687                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38688         });
38689     },
38690
38691     onRender: function(ctr, pos)
38692     {
38693         var dh = Roo.DomHelper;
38694         /** This region's container element 
38695         * @type Roo.Element */
38696         this.el = dh.append(ctr.dom, {
38697                 tag: "div",
38698                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38699             }, true);
38700         /** This region's title element 
38701         * @type Roo.Element */
38702     
38703         this.titleEl = dh.append(this.el.dom,  {
38704                 tag: "div",
38705                 unselectable: "on",
38706                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38707                 children:[
38708                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38709                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38710                 ]
38711             }, true);
38712         
38713         this.titleEl.enableDisplayMode();
38714         /** This region's title text element 
38715         * @type HTMLElement */
38716         this.titleTextEl = this.titleEl.dom.firstChild;
38717         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38718         /*
38719         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38720         this.closeBtn.enableDisplayMode();
38721         this.closeBtn.on("click", this.closeClicked, this);
38722         this.closeBtn.hide();
38723     */
38724         this.createBody(this.config);
38725         if(this.config.hideWhenEmpty){
38726             this.hide();
38727             this.on("paneladded", this.validateVisibility, this);
38728             this.on("panelremoved", this.validateVisibility, this);
38729         }
38730         if(this.autoScroll){
38731             this.bodyEl.setStyle("overflow", "auto");
38732         }else{
38733             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38734         }
38735         //if(c.titlebar !== false){
38736             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38737                 this.titleEl.hide();
38738             }else{
38739                 this.titleEl.show();
38740                 if(this.config.title){
38741                     this.titleTextEl.innerHTML = this.config.title;
38742                 }
38743             }
38744         //}
38745         if(this.config.collapsed){
38746             this.collapse(true);
38747         }
38748         if(this.config.hidden){
38749             this.hide();
38750         }
38751         
38752         if (this.unrendered_panels && this.unrendered_panels.length) {
38753             for (var i =0;i< this.unrendered_panels.length; i++) {
38754                 this.add(this.unrendered_panels[i]);
38755             }
38756             this.unrendered_panels = null;
38757             
38758         }
38759         
38760     },
38761     
38762     applyConfig : function(c)
38763     {
38764         /*
38765          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38766             var dh = Roo.DomHelper;
38767             if(c.titlebar !== false){
38768                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38769                 this.collapseBtn.on("click", this.collapse, this);
38770                 this.collapseBtn.enableDisplayMode();
38771                 /*
38772                 if(c.showPin === true || this.showPin){
38773                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38774                     this.stickBtn.enableDisplayMode();
38775                     this.stickBtn.on("click", this.expand, this);
38776                     this.stickBtn.hide();
38777                 }
38778                 
38779             }
38780             */
38781             /** This region's collapsed element
38782             * @type Roo.Element */
38783             /*
38784              *
38785             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38786                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38787             ]}, true);
38788             
38789             if(c.floatable !== false){
38790                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38791                this.collapsedEl.on("click", this.collapseClick, this);
38792             }
38793
38794             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38795                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38796                    id: "message", unselectable: "on", style:{"float":"left"}});
38797                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38798              }
38799             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38800             this.expandBtn.on("click", this.expand, this);
38801             
38802         }
38803         
38804         if(this.collapseBtn){
38805             this.collapseBtn.setVisible(c.collapsible == true);
38806         }
38807         
38808         this.cmargins = c.cmargins || this.cmargins ||
38809                          (this.position == "west" || this.position == "east" ?
38810                              {top: 0, left: 2, right:2, bottom: 0} :
38811                              {top: 2, left: 0, right:0, bottom: 2});
38812         */
38813         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38814         
38815         
38816         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38817         
38818         this.autoScroll = c.autoScroll || false;
38819         
38820         
38821        
38822         
38823         this.duration = c.duration || .30;
38824         this.slideDuration = c.slideDuration || .45;
38825         this.config = c;
38826        
38827     },
38828     /**
38829      * Returns true if this region is currently visible.
38830      * @return {Boolean}
38831      */
38832     isVisible : function(){
38833         return this.visible;
38834     },
38835
38836     /**
38837      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38838      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38839      */
38840     //setCollapsedTitle : function(title){
38841     //    title = title || "&#160;";
38842      //   if(this.collapsedTitleTextEl){
38843       //      this.collapsedTitleTextEl.innerHTML = title;
38844        // }
38845     //},
38846
38847     getBox : function(){
38848         var b;
38849       //  if(!this.collapsed){
38850             b = this.el.getBox(false, true);
38851        // }else{
38852           //  b = this.collapsedEl.getBox(false, true);
38853         //}
38854         return b;
38855     },
38856
38857     getMargins : function(){
38858         return this.margins;
38859         //return this.collapsed ? this.cmargins : this.margins;
38860     },
38861 /*
38862     highlight : function(){
38863         this.el.addClass("x-layout-panel-dragover");
38864     },
38865
38866     unhighlight : function(){
38867         this.el.removeClass("x-layout-panel-dragover");
38868     },
38869 */
38870     updateBox : function(box)
38871     {
38872         if (!this.bodyEl) {
38873             return; // not rendered yet..
38874         }
38875         
38876         this.box = box;
38877         if(!this.collapsed){
38878             this.el.dom.style.left = box.x + "px";
38879             this.el.dom.style.top = box.y + "px";
38880             this.updateBody(box.width, box.height);
38881         }else{
38882             this.collapsedEl.dom.style.left = box.x + "px";
38883             this.collapsedEl.dom.style.top = box.y + "px";
38884             this.collapsedEl.setSize(box.width, box.height);
38885         }
38886         if(this.tabs){
38887             this.tabs.autoSizeTabs();
38888         }
38889     },
38890
38891     updateBody : function(w, h)
38892     {
38893         if(w !== null){
38894             this.el.setWidth(w);
38895             w -= this.el.getBorderWidth("rl");
38896             if(this.config.adjustments){
38897                 w += this.config.adjustments[0];
38898             }
38899         }
38900         if(h !== null && h > 0){
38901             this.el.setHeight(h);
38902             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38903             h -= this.el.getBorderWidth("tb");
38904             if(this.config.adjustments){
38905                 h += this.config.adjustments[1];
38906             }
38907             this.bodyEl.setHeight(h);
38908             if(this.tabs){
38909                 h = this.tabs.syncHeight(h);
38910             }
38911         }
38912         if(this.panelSize){
38913             w = w !== null ? w : this.panelSize.width;
38914             h = h !== null ? h : this.panelSize.height;
38915         }
38916         if(this.activePanel){
38917             var el = this.activePanel.getEl();
38918             w = w !== null ? w : el.getWidth();
38919             h = h !== null ? h : el.getHeight();
38920             this.panelSize = {width: w, height: h};
38921             this.activePanel.setSize(w, h);
38922         }
38923         if(Roo.isIE && this.tabs){
38924             this.tabs.el.repaint();
38925         }
38926     },
38927
38928     /**
38929      * Returns the container element for this region.
38930      * @return {Roo.Element}
38931      */
38932     getEl : function(){
38933         return this.el;
38934     },
38935
38936     /**
38937      * Hides this region.
38938      */
38939     hide : function(){
38940         //if(!this.collapsed){
38941             this.el.dom.style.left = "-2000px";
38942             this.el.hide();
38943         //}else{
38944          //   this.collapsedEl.dom.style.left = "-2000px";
38945          //   this.collapsedEl.hide();
38946        // }
38947         this.visible = false;
38948         this.fireEvent("visibilitychange", this, false);
38949     },
38950
38951     /**
38952      * Shows this region if it was previously hidden.
38953      */
38954     show : function(){
38955         //if(!this.collapsed){
38956             this.el.show();
38957         //}else{
38958         //    this.collapsedEl.show();
38959        // }
38960         this.visible = true;
38961         this.fireEvent("visibilitychange", this, true);
38962     },
38963 /*
38964     closeClicked : function(){
38965         if(this.activePanel){
38966             this.remove(this.activePanel);
38967         }
38968     },
38969
38970     collapseClick : function(e){
38971         if(this.isSlid){
38972            e.stopPropagation();
38973            this.slideIn();
38974         }else{
38975            e.stopPropagation();
38976            this.slideOut();
38977         }
38978     },
38979 */
38980     /**
38981      * Collapses this region.
38982      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38983      */
38984     /*
38985     collapse : function(skipAnim, skipCheck = false){
38986         if(this.collapsed) {
38987             return;
38988         }
38989         
38990         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38991             
38992             this.collapsed = true;
38993             if(this.split){
38994                 this.split.el.hide();
38995             }
38996             if(this.config.animate && skipAnim !== true){
38997                 this.fireEvent("invalidated", this);
38998                 this.animateCollapse();
38999             }else{
39000                 this.el.setLocation(-20000,-20000);
39001                 this.el.hide();
39002                 this.collapsedEl.show();
39003                 this.fireEvent("collapsed", this);
39004                 this.fireEvent("invalidated", this);
39005             }
39006         }
39007         
39008     },
39009 */
39010     animateCollapse : function(){
39011         // overridden
39012     },
39013
39014     /**
39015      * Expands this region if it was previously collapsed.
39016      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39017      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39018      */
39019     /*
39020     expand : function(e, skipAnim){
39021         if(e) {
39022             e.stopPropagation();
39023         }
39024         if(!this.collapsed || this.el.hasActiveFx()) {
39025             return;
39026         }
39027         if(this.isSlid){
39028             this.afterSlideIn();
39029             skipAnim = true;
39030         }
39031         this.collapsed = false;
39032         if(this.config.animate && skipAnim !== true){
39033             this.animateExpand();
39034         }else{
39035             this.el.show();
39036             if(this.split){
39037                 this.split.el.show();
39038             }
39039             this.collapsedEl.setLocation(-2000,-2000);
39040             this.collapsedEl.hide();
39041             this.fireEvent("invalidated", this);
39042             this.fireEvent("expanded", this);
39043         }
39044     },
39045 */
39046     animateExpand : function(){
39047         // overridden
39048     },
39049
39050     initTabs : function()
39051     {
39052         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39053         
39054         var ts = new Roo.bootstrap.panel.Tabs({
39055             el: this.bodyEl.dom,
39056             region : this,
39057             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39058             disableTooltips: this.config.disableTabTips,
39059             toolbar : this.config.toolbar
39060         });
39061         
39062         if(this.config.hideTabs){
39063             ts.stripWrap.setDisplayed(false);
39064         }
39065         this.tabs = ts;
39066         ts.resizeTabs = this.config.resizeTabs === true;
39067         ts.minTabWidth = this.config.minTabWidth || 40;
39068         ts.maxTabWidth = this.config.maxTabWidth || 250;
39069         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39070         ts.monitorResize = false;
39071         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39072         ts.bodyEl.addClass('roo-layout-tabs-body');
39073         this.panels.each(this.initPanelAsTab, this);
39074     },
39075
39076     initPanelAsTab : function(panel){
39077         var ti = this.tabs.addTab(
39078             panel.getEl().id,
39079             panel.getTitle(),
39080             null,
39081             this.config.closeOnTab && panel.isClosable(),
39082             panel.tpl
39083         );
39084         if(panel.tabTip !== undefined){
39085             ti.setTooltip(panel.tabTip);
39086         }
39087         ti.on("activate", function(){
39088               this.setActivePanel(panel);
39089         }, this);
39090         
39091         if(this.config.closeOnTab){
39092             ti.on("beforeclose", function(t, e){
39093                 e.cancel = true;
39094                 this.remove(panel);
39095             }, this);
39096         }
39097         
39098         panel.tabItem = ti;
39099         
39100         return ti;
39101     },
39102
39103     updatePanelTitle : function(panel, title)
39104     {
39105         if(this.activePanel == panel){
39106             this.updateTitle(title);
39107         }
39108         if(this.tabs){
39109             var ti = this.tabs.getTab(panel.getEl().id);
39110             ti.setText(title);
39111             if(panel.tabTip !== undefined){
39112                 ti.setTooltip(panel.tabTip);
39113             }
39114         }
39115     },
39116
39117     updateTitle : function(title){
39118         if(this.titleTextEl && !this.config.title){
39119             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39120         }
39121     },
39122
39123     setActivePanel : function(panel)
39124     {
39125         panel = this.getPanel(panel);
39126         if(this.activePanel && this.activePanel != panel){
39127             if(this.activePanel.setActiveState(false) === false){
39128                 return;
39129             }
39130         }
39131         this.activePanel = panel;
39132         panel.setActiveState(true);
39133         if(this.panelSize){
39134             panel.setSize(this.panelSize.width, this.panelSize.height);
39135         }
39136         if(this.closeBtn){
39137             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39138         }
39139         this.updateTitle(panel.getTitle());
39140         if(this.tabs){
39141             this.fireEvent("invalidated", this);
39142         }
39143         this.fireEvent("panelactivated", this, panel);
39144     },
39145
39146     /**
39147      * Shows the specified panel.
39148      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39149      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39150      */
39151     showPanel : function(panel)
39152     {
39153         panel = this.getPanel(panel);
39154         if(panel){
39155             if(this.tabs){
39156                 var tab = this.tabs.getTab(panel.getEl().id);
39157                 if(tab.isHidden()){
39158                     this.tabs.unhideTab(tab.id);
39159                 }
39160                 tab.activate();
39161             }else{
39162                 this.setActivePanel(panel);
39163             }
39164         }
39165         return panel;
39166     },
39167
39168     /**
39169      * Get the active panel for this region.
39170      * @return {Roo.ContentPanel} The active panel or null
39171      */
39172     getActivePanel : function(){
39173         return this.activePanel;
39174     },
39175
39176     validateVisibility : function(){
39177         if(this.panels.getCount() < 1){
39178             this.updateTitle("&#160;");
39179             this.closeBtn.hide();
39180             this.hide();
39181         }else{
39182             if(!this.isVisible()){
39183                 this.show();
39184             }
39185         }
39186     },
39187
39188     /**
39189      * Adds the passed ContentPanel(s) to this region.
39190      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39191      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39192      */
39193     add : function(panel)
39194     {
39195         if(arguments.length > 1){
39196             for(var i = 0, len = arguments.length; i < len; i++) {
39197                 this.add(arguments[i]);
39198             }
39199             return null;
39200         }
39201         
39202         // if we have not been rendered yet, then we can not really do much of this..
39203         if (!this.bodyEl) {
39204             this.unrendered_panels.push(panel);
39205             return panel;
39206         }
39207         
39208         
39209         
39210         
39211         if(this.hasPanel(panel)){
39212             this.showPanel(panel);
39213             return panel;
39214         }
39215         panel.setRegion(this);
39216         this.panels.add(panel);
39217        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39218             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39219             // and hide them... ???
39220             this.bodyEl.dom.appendChild(panel.getEl().dom);
39221             if(panel.background !== true){
39222                 this.setActivePanel(panel);
39223             }
39224             this.fireEvent("paneladded", this, panel);
39225             return panel;
39226         }
39227         */
39228         if(!this.tabs){
39229             this.initTabs();
39230         }else{
39231             this.initPanelAsTab(panel);
39232         }
39233         
39234         
39235         if(panel.background !== true){
39236             this.tabs.activate(panel.getEl().id);
39237         }
39238         this.fireEvent("paneladded", this, panel);
39239         return panel;
39240     },
39241
39242     /**
39243      * Hides the tab for the specified panel.
39244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39245      */
39246     hidePanel : function(panel){
39247         if(this.tabs && (panel = this.getPanel(panel))){
39248             this.tabs.hideTab(panel.getEl().id);
39249         }
39250     },
39251
39252     /**
39253      * Unhides the tab for a previously hidden panel.
39254      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39255      */
39256     unhidePanel : function(panel){
39257         if(this.tabs && (panel = this.getPanel(panel))){
39258             this.tabs.unhideTab(panel.getEl().id);
39259         }
39260     },
39261
39262     clearPanels : function(){
39263         while(this.panels.getCount() > 0){
39264              this.remove(this.panels.first());
39265         }
39266     },
39267
39268     /**
39269      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39270      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39271      * @param {Boolean} preservePanel Overrides the config preservePanel option
39272      * @return {Roo.ContentPanel} The panel that was removed
39273      */
39274     remove : function(panel, preservePanel)
39275     {
39276         panel = this.getPanel(panel);
39277         if(!panel){
39278             return null;
39279         }
39280         var e = {};
39281         this.fireEvent("beforeremove", this, panel, e);
39282         if(e.cancel === true){
39283             return null;
39284         }
39285         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39286         var panelId = panel.getId();
39287         this.panels.removeKey(panelId);
39288         if(preservePanel){
39289             document.body.appendChild(panel.getEl().dom);
39290         }
39291         if(this.tabs){
39292             this.tabs.removeTab(panel.getEl().id);
39293         }else if (!preservePanel){
39294             this.bodyEl.dom.removeChild(panel.getEl().dom);
39295         }
39296         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39297             var p = this.panels.first();
39298             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39299             tempEl.appendChild(p.getEl().dom);
39300             this.bodyEl.update("");
39301             this.bodyEl.dom.appendChild(p.getEl().dom);
39302             tempEl = null;
39303             this.updateTitle(p.getTitle());
39304             this.tabs = null;
39305             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39306             this.setActivePanel(p);
39307         }
39308         panel.setRegion(null);
39309         if(this.activePanel == panel){
39310             this.activePanel = null;
39311         }
39312         if(this.config.autoDestroy !== false && preservePanel !== true){
39313             try{panel.destroy();}catch(e){}
39314         }
39315         this.fireEvent("panelremoved", this, panel);
39316         return panel;
39317     },
39318
39319     /**
39320      * Returns the TabPanel component used by this region
39321      * @return {Roo.TabPanel}
39322      */
39323     getTabs : function(){
39324         return this.tabs;
39325     },
39326
39327     createTool : function(parentEl, className){
39328         var btn = Roo.DomHelper.append(parentEl, {
39329             tag: "div",
39330             cls: "x-layout-tools-button",
39331             children: [ {
39332                 tag: "div",
39333                 cls: "roo-layout-tools-button-inner " + className,
39334                 html: "&#160;"
39335             }]
39336         }, true);
39337         btn.addClassOnOver("roo-layout-tools-button-over");
39338         return btn;
39339     }
39340 });/*
39341  * Based on:
39342  * Ext JS Library 1.1.1
39343  * Copyright(c) 2006-2007, Ext JS, LLC.
39344  *
39345  * Originally Released Under LGPL - original licence link has changed is not relivant.
39346  *
39347  * Fork - LGPL
39348  * <script type="text/javascript">
39349  */
39350  
39351
39352
39353 /**
39354  * @class Roo.SplitLayoutRegion
39355  * @extends Roo.LayoutRegion
39356  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39357  */
39358 Roo.bootstrap.layout.Split = function(config){
39359     this.cursor = config.cursor;
39360     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39361 };
39362
39363 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39364 {
39365     splitTip : "Drag to resize.",
39366     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39367     useSplitTips : false,
39368
39369     applyConfig : function(config){
39370         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39371     },
39372     
39373     onRender : function(ctr,pos) {
39374         
39375         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39376         if(!this.config.split){
39377             return;
39378         }
39379         if(!this.split){
39380             
39381             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39382                             tag: "div",
39383                             id: this.el.id + "-split",
39384                             cls: "roo-layout-split roo-layout-split-"+this.position,
39385                             html: "&#160;"
39386             });
39387             /** The SplitBar for this region 
39388             * @type Roo.SplitBar */
39389             // does not exist yet...
39390             Roo.log([this.position, this.orientation]);
39391             
39392             this.split = new Roo.bootstrap.SplitBar({
39393                 dragElement : splitEl,
39394                 resizingElement: this.el,
39395                 orientation : this.orientation
39396             });
39397             
39398             this.split.on("moved", this.onSplitMove, this);
39399             this.split.useShim = this.config.useShim === true;
39400             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39401             if(this.useSplitTips){
39402                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39403             }
39404             //if(config.collapsible){
39405             //    this.split.el.on("dblclick", this.collapse,  this);
39406             //}
39407         }
39408         if(typeof this.config.minSize != "undefined"){
39409             this.split.minSize = this.config.minSize;
39410         }
39411         if(typeof this.config.maxSize != "undefined"){
39412             this.split.maxSize = this.config.maxSize;
39413         }
39414         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39415             this.hideSplitter();
39416         }
39417         
39418     },
39419
39420     getHMaxSize : function(){
39421          var cmax = this.config.maxSize || 10000;
39422          var center = this.mgr.getRegion("center");
39423          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39424     },
39425
39426     getVMaxSize : function(){
39427          var cmax = this.config.maxSize || 10000;
39428          var center = this.mgr.getRegion("center");
39429          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39430     },
39431
39432     onSplitMove : function(split, newSize){
39433         this.fireEvent("resized", this, newSize);
39434     },
39435     
39436     /** 
39437      * Returns the {@link Roo.SplitBar} for this region.
39438      * @return {Roo.SplitBar}
39439      */
39440     getSplitBar : function(){
39441         return this.split;
39442     },
39443     
39444     hide : function(){
39445         this.hideSplitter();
39446         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39447     },
39448
39449     hideSplitter : function(){
39450         if(this.split){
39451             this.split.el.setLocation(-2000,-2000);
39452             this.split.el.hide();
39453         }
39454     },
39455
39456     show : function(){
39457         if(this.split){
39458             this.split.el.show();
39459         }
39460         Roo.bootstrap.layout.Split.superclass.show.call(this);
39461     },
39462     
39463     beforeSlide: function(){
39464         if(Roo.isGecko){// firefox overflow auto bug workaround
39465             this.bodyEl.clip();
39466             if(this.tabs) {
39467                 this.tabs.bodyEl.clip();
39468             }
39469             if(this.activePanel){
39470                 this.activePanel.getEl().clip();
39471                 
39472                 if(this.activePanel.beforeSlide){
39473                     this.activePanel.beforeSlide();
39474                 }
39475             }
39476         }
39477     },
39478     
39479     afterSlide : function(){
39480         if(Roo.isGecko){// firefox overflow auto bug workaround
39481             this.bodyEl.unclip();
39482             if(this.tabs) {
39483                 this.tabs.bodyEl.unclip();
39484             }
39485             if(this.activePanel){
39486                 this.activePanel.getEl().unclip();
39487                 if(this.activePanel.afterSlide){
39488                     this.activePanel.afterSlide();
39489                 }
39490             }
39491         }
39492     },
39493
39494     initAutoHide : function(){
39495         if(this.autoHide !== false){
39496             if(!this.autoHideHd){
39497                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39498                 this.autoHideHd = {
39499                     "mouseout": function(e){
39500                         if(!e.within(this.el, true)){
39501                             st.delay(500);
39502                         }
39503                     },
39504                     "mouseover" : function(e){
39505                         st.cancel();
39506                     },
39507                     scope : this
39508                 };
39509             }
39510             this.el.on(this.autoHideHd);
39511         }
39512     },
39513
39514     clearAutoHide : function(){
39515         if(this.autoHide !== false){
39516             this.el.un("mouseout", this.autoHideHd.mouseout);
39517             this.el.un("mouseover", this.autoHideHd.mouseover);
39518         }
39519     },
39520
39521     clearMonitor : function(){
39522         Roo.get(document).un("click", this.slideInIf, this);
39523     },
39524
39525     // these names are backwards but not changed for compat
39526     slideOut : function(){
39527         if(this.isSlid || this.el.hasActiveFx()){
39528             return;
39529         }
39530         this.isSlid = true;
39531         if(this.collapseBtn){
39532             this.collapseBtn.hide();
39533         }
39534         this.closeBtnState = this.closeBtn.getStyle('display');
39535         this.closeBtn.hide();
39536         if(this.stickBtn){
39537             this.stickBtn.show();
39538         }
39539         this.el.show();
39540         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39541         this.beforeSlide();
39542         this.el.setStyle("z-index", 10001);
39543         this.el.slideIn(this.getSlideAnchor(), {
39544             callback: function(){
39545                 this.afterSlide();
39546                 this.initAutoHide();
39547                 Roo.get(document).on("click", this.slideInIf, this);
39548                 this.fireEvent("slideshow", this);
39549             },
39550             scope: this,
39551             block: true
39552         });
39553     },
39554
39555     afterSlideIn : function(){
39556         this.clearAutoHide();
39557         this.isSlid = false;
39558         this.clearMonitor();
39559         this.el.setStyle("z-index", "");
39560         if(this.collapseBtn){
39561             this.collapseBtn.show();
39562         }
39563         this.closeBtn.setStyle('display', this.closeBtnState);
39564         if(this.stickBtn){
39565             this.stickBtn.hide();
39566         }
39567         this.fireEvent("slidehide", this);
39568     },
39569
39570     slideIn : function(cb){
39571         if(!this.isSlid || this.el.hasActiveFx()){
39572             Roo.callback(cb);
39573             return;
39574         }
39575         this.isSlid = false;
39576         this.beforeSlide();
39577         this.el.slideOut(this.getSlideAnchor(), {
39578             callback: function(){
39579                 this.el.setLeftTop(-10000, -10000);
39580                 this.afterSlide();
39581                 this.afterSlideIn();
39582                 Roo.callback(cb);
39583             },
39584             scope: this,
39585             block: true
39586         });
39587     },
39588     
39589     slideInIf : function(e){
39590         if(!e.within(this.el)){
39591             this.slideIn();
39592         }
39593     },
39594
39595     animateCollapse : function(){
39596         this.beforeSlide();
39597         this.el.setStyle("z-index", 20000);
39598         var anchor = this.getSlideAnchor();
39599         this.el.slideOut(anchor, {
39600             callback : function(){
39601                 this.el.setStyle("z-index", "");
39602                 this.collapsedEl.slideIn(anchor, {duration:.3});
39603                 this.afterSlide();
39604                 this.el.setLocation(-10000,-10000);
39605                 this.el.hide();
39606                 this.fireEvent("collapsed", this);
39607             },
39608             scope: this,
39609             block: true
39610         });
39611     },
39612
39613     animateExpand : function(){
39614         this.beforeSlide();
39615         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39616         this.el.setStyle("z-index", 20000);
39617         this.collapsedEl.hide({
39618             duration:.1
39619         });
39620         this.el.slideIn(this.getSlideAnchor(), {
39621             callback : function(){
39622                 this.el.setStyle("z-index", "");
39623                 this.afterSlide();
39624                 if(this.split){
39625                     this.split.el.show();
39626                 }
39627                 this.fireEvent("invalidated", this);
39628                 this.fireEvent("expanded", this);
39629             },
39630             scope: this,
39631             block: true
39632         });
39633     },
39634
39635     anchors : {
39636         "west" : "left",
39637         "east" : "right",
39638         "north" : "top",
39639         "south" : "bottom"
39640     },
39641
39642     sanchors : {
39643         "west" : "l",
39644         "east" : "r",
39645         "north" : "t",
39646         "south" : "b"
39647     },
39648
39649     canchors : {
39650         "west" : "tl-tr",
39651         "east" : "tr-tl",
39652         "north" : "tl-bl",
39653         "south" : "bl-tl"
39654     },
39655
39656     getAnchor : function(){
39657         return this.anchors[this.position];
39658     },
39659
39660     getCollapseAnchor : function(){
39661         return this.canchors[this.position];
39662     },
39663
39664     getSlideAnchor : function(){
39665         return this.sanchors[this.position];
39666     },
39667
39668     getAlignAdj : function(){
39669         var cm = this.cmargins;
39670         switch(this.position){
39671             case "west":
39672                 return [0, 0];
39673             break;
39674             case "east":
39675                 return [0, 0];
39676             break;
39677             case "north":
39678                 return [0, 0];
39679             break;
39680             case "south":
39681                 return [0, 0];
39682             break;
39683         }
39684     },
39685
39686     getExpandAdj : function(){
39687         var c = this.collapsedEl, cm = this.cmargins;
39688         switch(this.position){
39689             case "west":
39690                 return [-(cm.right+c.getWidth()+cm.left), 0];
39691             break;
39692             case "east":
39693                 return [cm.right+c.getWidth()+cm.left, 0];
39694             break;
39695             case "north":
39696                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39697             break;
39698             case "south":
39699                 return [0, cm.top+cm.bottom+c.getHeight()];
39700             break;
39701         }
39702     }
39703 });/*
39704  * Based on:
39705  * Ext JS Library 1.1.1
39706  * Copyright(c) 2006-2007, Ext JS, LLC.
39707  *
39708  * Originally Released Under LGPL - original licence link has changed is not relivant.
39709  *
39710  * Fork - LGPL
39711  * <script type="text/javascript">
39712  */
39713 /*
39714  * These classes are private internal classes
39715  */
39716 Roo.bootstrap.layout.Center = function(config){
39717     config.region = "center";
39718     Roo.bootstrap.layout.Region.call(this, config);
39719     this.visible = true;
39720     this.minWidth = config.minWidth || 20;
39721     this.minHeight = config.minHeight || 20;
39722 };
39723
39724 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39725     hide : function(){
39726         // center panel can't be hidden
39727     },
39728     
39729     show : function(){
39730         // center panel can't be hidden
39731     },
39732     
39733     getMinWidth: function(){
39734         return this.minWidth;
39735     },
39736     
39737     getMinHeight: function(){
39738         return this.minHeight;
39739     }
39740 });
39741
39742
39743
39744
39745  
39746
39747
39748
39749
39750
39751
39752 Roo.bootstrap.layout.North = function(config)
39753 {
39754     config.region = 'north';
39755     config.cursor = 'n-resize';
39756     
39757     Roo.bootstrap.layout.Split.call(this, config);
39758     
39759     
39760     if(this.split){
39761         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39762         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39763         this.split.el.addClass("roo-layout-split-v");
39764     }
39765     //var size = config.initialSize || config.height;
39766     //if(this.el && typeof size != "undefined"){
39767     //    this.el.setHeight(size);
39768     //}
39769 };
39770 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39771 {
39772     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39773      
39774      
39775     onRender : function(ctr, pos)
39776     {
39777         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39778         var size = this.config.initialSize || this.config.height;
39779         if(this.el && typeof size != "undefined"){
39780             this.el.setHeight(size);
39781         }
39782     
39783     },
39784     
39785     getBox : function(){
39786         if(this.collapsed){
39787             return this.collapsedEl.getBox();
39788         }
39789         var box = this.el.getBox();
39790         if(this.split){
39791             box.height += this.split.el.getHeight();
39792         }
39793         return box;
39794     },
39795     
39796     updateBox : function(box){
39797         if(this.split && !this.collapsed){
39798             box.height -= this.split.el.getHeight();
39799             this.split.el.setLeft(box.x);
39800             this.split.el.setTop(box.y+box.height);
39801             this.split.el.setWidth(box.width);
39802         }
39803         if(this.collapsed){
39804             this.updateBody(box.width, null);
39805         }
39806         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39807     }
39808 });
39809
39810
39811
39812
39813
39814 Roo.bootstrap.layout.South = function(config){
39815     config.region = 'south';
39816     config.cursor = 's-resize';
39817     Roo.bootstrap.layout.Split.call(this, config);
39818     if(this.split){
39819         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39820         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39821         this.split.el.addClass("roo-layout-split-v");
39822     }
39823     
39824 };
39825
39826 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39827     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39828     
39829     onRender : function(ctr, pos)
39830     {
39831         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39832         var size = this.config.initialSize || this.config.height;
39833         if(this.el && typeof size != "undefined"){
39834             this.el.setHeight(size);
39835         }
39836     
39837     },
39838     
39839     getBox : function(){
39840         if(this.collapsed){
39841             return this.collapsedEl.getBox();
39842         }
39843         var box = this.el.getBox();
39844         if(this.split){
39845             var sh = this.split.el.getHeight();
39846             box.height += sh;
39847             box.y -= sh;
39848         }
39849         return box;
39850     },
39851     
39852     updateBox : function(box){
39853         if(this.split && !this.collapsed){
39854             var sh = this.split.el.getHeight();
39855             box.height -= sh;
39856             box.y += sh;
39857             this.split.el.setLeft(box.x);
39858             this.split.el.setTop(box.y-sh);
39859             this.split.el.setWidth(box.width);
39860         }
39861         if(this.collapsed){
39862             this.updateBody(box.width, null);
39863         }
39864         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39865     }
39866 });
39867
39868 Roo.bootstrap.layout.East = function(config){
39869     config.region = "east";
39870     config.cursor = "e-resize";
39871     Roo.bootstrap.layout.Split.call(this, config);
39872     if(this.split){
39873         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39874         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39875         this.split.el.addClass("roo-layout-split-h");
39876     }
39877     
39878 };
39879 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39880     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39881     
39882     onRender : function(ctr, pos)
39883     {
39884         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39885         var size = this.config.initialSize || this.config.width;
39886         if(this.el && typeof size != "undefined"){
39887             this.el.setWidth(size);
39888         }
39889     
39890     },
39891     
39892     getBox : function(){
39893         if(this.collapsed){
39894             return this.collapsedEl.getBox();
39895         }
39896         var box = this.el.getBox();
39897         if(this.split){
39898             var sw = this.split.el.getWidth();
39899             box.width += sw;
39900             box.x -= sw;
39901         }
39902         return box;
39903     },
39904
39905     updateBox : function(box){
39906         if(this.split && !this.collapsed){
39907             var sw = this.split.el.getWidth();
39908             box.width -= sw;
39909             this.split.el.setLeft(box.x);
39910             this.split.el.setTop(box.y);
39911             this.split.el.setHeight(box.height);
39912             box.x += sw;
39913         }
39914         if(this.collapsed){
39915             this.updateBody(null, box.height);
39916         }
39917         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39918     }
39919 });
39920
39921 Roo.bootstrap.layout.West = function(config){
39922     config.region = "west";
39923     config.cursor = "w-resize";
39924     
39925     Roo.bootstrap.layout.Split.call(this, config);
39926     if(this.split){
39927         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39928         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39929         this.split.el.addClass("roo-layout-split-h");
39930     }
39931     
39932 };
39933 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39934     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39935     
39936     onRender: function(ctr, pos)
39937     {
39938         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39939         var size = this.config.initialSize || this.config.width;
39940         if(typeof size != "undefined"){
39941             this.el.setWidth(size);
39942         }
39943     },
39944     
39945     getBox : function(){
39946         if(this.collapsed){
39947             return this.collapsedEl.getBox();
39948         }
39949         var box = this.el.getBox();
39950         if (box.width == 0) {
39951             box.width = this.config.width; // kludge?
39952         }
39953         if(this.split){
39954             box.width += this.split.el.getWidth();
39955         }
39956         return box;
39957     },
39958     
39959     updateBox : function(box){
39960         if(this.split && !this.collapsed){
39961             var sw = this.split.el.getWidth();
39962             box.width -= sw;
39963             this.split.el.setLeft(box.x+box.width);
39964             this.split.el.setTop(box.y);
39965             this.split.el.setHeight(box.height);
39966         }
39967         if(this.collapsed){
39968             this.updateBody(null, box.height);
39969         }
39970         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39971     }
39972 });Roo.namespace("Roo.bootstrap.panel");/*
39973  * Based on:
39974  * Ext JS Library 1.1.1
39975  * Copyright(c) 2006-2007, Ext JS, LLC.
39976  *
39977  * Originally Released Under LGPL - original licence link has changed is not relivant.
39978  *
39979  * Fork - LGPL
39980  * <script type="text/javascript">
39981  */
39982 /**
39983  * @class Roo.ContentPanel
39984  * @extends Roo.util.Observable
39985  * A basic ContentPanel element.
39986  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39987  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39988  * @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
39989  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39990  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39991  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39992  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39993  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39994  * @cfg {String} title          The title for this panel
39995  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39996  * @cfg {String} url            Calls {@link #setUrl} with this value
39997  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39998  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39999  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40000  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40001  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40002  * @cfg {Boolean} badges render the badges
40003  * @cfg {String} cls  extra classes to use  
40004  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40005
40006  * @constructor
40007  * Create a new ContentPanel.
40008  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40009  * @param {String/Object} config A string to set only the title or a config object
40010  * @param {String} content (optional) Set the HTML content for this panel
40011  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40012  */
40013 Roo.bootstrap.panel.Content = function( config){
40014     
40015     this.tpl = config.tpl || false;
40016     
40017     var el = config.el;
40018     var content = config.content;
40019
40020     if(config.autoCreate){ // xtype is available if this is called from factory
40021         el = Roo.id();
40022     }
40023     this.el = Roo.get(el);
40024     if(!this.el && config && config.autoCreate){
40025         if(typeof config.autoCreate == "object"){
40026             if(!config.autoCreate.id){
40027                 config.autoCreate.id = config.id||el;
40028             }
40029             this.el = Roo.DomHelper.append(document.body,
40030                         config.autoCreate, true);
40031         }else{
40032             var elcfg =  {
40033                 tag: "div",
40034                 cls: (config.cls || '') +
40035                     (config.background ? ' bg-' + config.background : '') +
40036                     " roo-layout-inactive-content",
40037                 id: config.id||el
40038             };
40039             if (config.iframe) {
40040                 elcfg.cn = [
40041                     {
40042                         tag : 'iframe',
40043                         style : 'border: 0px',
40044                         src : 'about:blank'
40045                     }
40046                 ];
40047             }
40048               
40049             if (config.html) {
40050                 elcfg.html = config.html;
40051                 
40052             }
40053                         
40054             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40055             if (config.iframe) {
40056                 this.iframeEl = this.el.select('iframe',true).first();
40057             }
40058             
40059         }
40060     } 
40061     this.closable = false;
40062     this.loaded = false;
40063     this.active = false;
40064    
40065       
40066     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40067         
40068         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40069         
40070         this.wrapEl = this.el; //this.el.wrap();
40071         var ti = [];
40072         if (config.toolbar.items) {
40073             ti = config.toolbar.items ;
40074             delete config.toolbar.items ;
40075         }
40076         
40077         var nitems = [];
40078         this.toolbar.render(this.wrapEl, 'before');
40079         for(var i =0;i < ti.length;i++) {
40080           //  Roo.log(['add child', items[i]]);
40081             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40082         }
40083         this.toolbar.items = nitems;
40084         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40085         delete config.toolbar;
40086         
40087     }
40088     /*
40089     // xtype created footer. - not sure if will work as we normally have to render first..
40090     if (this.footer && !this.footer.el && this.footer.xtype) {
40091         if (!this.wrapEl) {
40092             this.wrapEl = this.el.wrap();
40093         }
40094     
40095         this.footer.container = this.wrapEl.createChild();
40096          
40097         this.footer = Roo.factory(this.footer, Roo);
40098         
40099     }
40100     */
40101     
40102      if(typeof config == "string"){
40103         this.title = config;
40104     }else{
40105         Roo.apply(this, config);
40106     }
40107     
40108     if(this.resizeEl){
40109         this.resizeEl = Roo.get(this.resizeEl, true);
40110     }else{
40111         this.resizeEl = this.el;
40112     }
40113     // handle view.xtype
40114     
40115  
40116     
40117     
40118     this.addEvents({
40119         /**
40120          * @event activate
40121          * Fires when this panel is activated. 
40122          * @param {Roo.ContentPanel} this
40123          */
40124         "activate" : true,
40125         /**
40126          * @event deactivate
40127          * Fires when this panel is activated. 
40128          * @param {Roo.ContentPanel} this
40129          */
40130         "deactivate" : true,
40131
40132         /**
40133          * @event resize
40134          * Fires when this panel is resized if fitToFrame is true.
40135          * @param {Roo.ContentPanel} this
40136          * @param {Number} width The width after any component adjustments
40137          * @param {Number} height The height after any component adjustments
40138          */
40139         "resize" : true,
40140         
40141          /**
40142          * @event render
40143          * Fires when this tab is created
40144          * @param {Roo.ContentPanel} this
40145          */
40146         "render" : true
40147         
40148         
40149         
40150     });
40151     
40152
40153     
40154     
40155     if(this.autoScroll && !this.iframe){
40156         this.resizeEl.setStyle("overflow", "auto");
40157     } else {
40158         // fix randome scrolling
40159         //this.el.on('scroll', function() {
40160         //    Roo.log('fix random scolling');
40161         //    this.scrollTo('top',0); 
40162         //});
40163     }
40164     content = content || this.content;
40165     if(content){
40166         this.setContent(content);
40167     }
40168     if(config && config.url){
40169         this.setUrl(this.url, this.params, this.loadOnce);
40170     }
40171     
40172     
40173     
40174     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40175     
40176     if (this.view && typeof(this.view.xtype) != 'undefined') {
40177         this.view.el = this.el.appendChild(document.createElement("div"));
40178         this.view = Roo.factory(this.view); 
40179         this.view.render  &&  this.view.render(false, '');  
40180     }
40181     
40182     
40183     this.fireEvent('render', this);
40184 };
40185
40186 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40187     
40188     cls : '',
40189     background : '',
40190     
40191     tabTip : '',
40192     
40193     iframe : false,
40194     iframeEl : false,
40195     
40196     setRegion : function(region){
40197         this.region = region;
40198         this.setActiveClass(region && !this.background);
40199     },
40200     
40201     
40202     setActiveClass: function(state)
40203     {
40204         if(state){
40205            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40206            this.el.setStyle('position','relative');
40207         }else{
40208            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40209            this.el.setStyle('position', 'absolute');
40210         } 
40211     },
40212     
40213     /**
40214      * Returns the toolbar for this Panel if one was configured. 
40215      * @return {Roo.Toolbar} 
40216      */
40217     getToolbar : function(){
40218         return this.toolbar;
40219     },
40220     
40221     setActiveState : function(active)
40222     {
40223         this.active = active;
40224         this.setActiveClass(active);
40225         if(!active){
40226             if(this.fireEvent("deactivate", this) === false){
40227                 return false;
40228             }
40229             return true;
40230         }
40231         this.fireEvent("activate", this);
40232         return true;
40233     },
40234     /**
40235      * Updates this panel's element (not for iframe)
40236      * @param {String} content The new content
40237      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40238     */
40239     setContent : function(content, loadScripts){
40240         if (this.iframe) {
40241             return;
40242         }
40243         
40244         this.el.update(content, loadScripts);
40245     },
40246
40247     ignoreResize : function(w, h){
40248         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40249             return true;
40250         }else{
40251             this.lastSize = {width: w, height: h};
40252             return false;
40253         }
40254     },
40255     /**
40256      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40257      * @return {Roo.UpdateManager} The UpdateManager
40258      */
40259     getUpdateManager : function(){
40260         if (this.iframe) {
40261             return false;
40262         }
40263         return this.el.getUpdateManager();
40264     },
40265      /**
40266      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40267      * Does not work with IFRAME contents
40268      * @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:
40269 <pre><code>
40270 panel.load({
40271     url: "your-url.php",
40272     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40273     callback: yourFunction,
40274     scope: yourObject, //(optional scope)
40275     discardUrl: false,
40276     nocache: false,
40277     text: "Loading...",
40278     timeout: 30,
40279     scripts: false
40280 });
40281 </code></pre>
40282      
40283      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40284      * 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.
40285      * @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}
40286      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40287      * @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.
40288      * @return {Roo.ContentPanel} this
40289      */
40290     load : function(){
40291         
40292         if (this.iframe) {
40293             return this;
40294         }
40295         
40296         var um = this.el.getUpdateManager();
40297         um.update.apply(um, arguments);
40298         return this;
40299     },
40300
40301
40302     /**
40303      * 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.
40304      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40305      * @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)
40306      * @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)
40307      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40308      */
40309     setUrl : function(url, params, loadOnce){
40310         if (this.iframe) {
40311             this.iframeEl.dom.src = url;
40312             return false;
40313         }
40314         
40315         if(this.refreshDelegate){
40316             this.removeListener("activate", this.refreshDelegate);
40317         }
40318         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40319         this.on("activate", this.refreshDelegate);
40320         return this.el.getUpdateManager();
40321     },
40322     
40323     _handleRefresh : function(url, params, loadOnce){
40324         if(!loadOnce || !this.loaded){
40325             var updater = this.el.getUpdateManager();
40326             updater.update(url, params, this._setLoaded.createDelegate(this));
40327         }
40328     },
40329     
40330     _setLoaded : function(){
40331         this.loaded = true;
40332     }, 
40333     
40334     /**
40335      * Returns this panel's id
40336      * @return {String} 
40337      */
40338     getId : function(){
40339         return this.el.id;
40340     },
40341     
40342     /** 
40343      * Returns this panel's element - used by regiosn to add.
40344      * @return {Roo.Element} 
40345      */
40346     getEl : function(){
40347         return this.wrapEl || this.el;
40348     },
40349     
40350    
40351     
40352     adjustForComponents : function(width, height)
40353     {
40354         //Roo.log('adjustForComponents ');
40355         if(this.resizeEl != this.el){
40356             width -= this.el.getFrameWidth('lr');
40357             height -= this.el.getFrameWidth('tb');
40358         }
40359         if(this.toolbar){
40360             var te = this.toolbar.getEl();
40361             te.setWidth(width);
40362             height -= te.getHeight();
40363         }
40364         if(this.footer){
40365             var te = this.footer.getEl();
40366             te.setWidth(width);
40367             height -= te.getHeight();
40368         }
40369         
40370         
40371         if(this.adjustments){
40372             width += this.adjustments[0];
40373             height += this.adjustments[1];
40374         }
40375         return {"width": width, "height": height};
40376     },
40377     
40378     setSize : function(width, height){
40379         if(this.fitToFrame && !this.ignoreResize(width, height)){
40380             if(this.fitContainer && this.resizeEl != this.el){
40381                 this.el.setSize(width, height);
40382             }
40383             var size = this.adjustForComponents(width, height);
40384             if (this.iframe) {
40385                 this.iframeEl.setSize(width,height);
40386             }
40387             
40388             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40389             this.fireEvent('resize', this, size.width, size.height);
40390             
40391             
40392         }
40393     },
40394     
40395     /**
40396      * Returns this panel's title
40397      * @return {String} 
40398      */
40399     getTitle : function(){
40400         
40401         if (typeof(this.title) != 'object') {
40402             return this.title;
40403         }
40404         
40405         var t = '';
40406         for (var k in this.title) {
40407             if (!this.title.hasOwnProperty(k)) {
40408                 continue;
40409             }
40410             
40411             if (k.indexOf('-') >= 0) {
40412                 var s = k.split('-');
40413                 for (var i = 0; i<s.length; i++) {
40414                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40415                 }
40416             } else {
40417                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40418             }
40419         }
40420         return t;
40421     },
40422     
40423     /**
40424      * Set this panel's title
40425      * @param {String} title
40426      */
40427     setTitle : function(title){
40428         this.title = title;
40429         if(this.region){
40430             this.region.updatePanelTitle(this, title);
40431         }
40432     },
40433     
40434     /**
40435      * Returns true is this panel was configured to be closable
40436      * @return {Boolean} 
40437      */
40438     isClosable : function(){
40439         return this.closable;
40440     },
40441     
40442     beforeSlide : function(){
40443         this.el.clip();
40444         this.resizeEl.clip();
40445     },
40446     
40447     afterSlide : function(){
40448         this.el.unclip();
40449         this.resizeEl.unclip();
40450     },
40451     
40452     /**
40453      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40454      *   Will fail silently if the {@link #setUrl} method has not been called.
40455      *   This does not activate the panel, just updates its content.
40456      */
40457     refresh : function(){
40458         if(this.refreshDelegate){
40459            this.loaded = false;
40460            this.refreshDelegate();
40461         }
40462     },
40463     
40464     /**
40465      * Destroys this panel
40466      */
40467     destroy : function(){
40468         this.el.removeAllListeners();
40469         var tempEl = document.createElement("span");
40470         tempEl.appendChild(this.el.dom);
40471         tempEl.innerHTML = "";
40472         this.el.remove();
40473         this.el = null;
40474     },
40475     
40476     /**
40477      * form - if the content panel contains a form - this is a reference to it.
40478      * @type {Roo.form.Form}
40479      */
40480     form : false,
40481     /**
40482      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40483      *    This contains a reference to it.
40484      * @type {Roo.View}
40485      */
40486     view : false,
40487     
40488       /**
40489      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40490      * <pre><code>
40491
40492 layout.addxtype({
40493        xtype : 'Form',
40494        items: [ .... ]
40495    }
40496 );
40497
40498 </code></pre>
40499      * @param {Object} cfg Xtype definition of item to add.
40500      */
40501     
40502     
40503     getChildContainer: function () {
40504         return this.getEl();
40505     }
40506     
40507     
40508     /*
40509         var  ret = new Roo.factory(cfg);
40510         return ret;
40511         
40512         
40513         // add form..
40514         if (cfg.xtype.match(/^Form$/)) {
40515             
40516             var el;
40517             //if (this.footer) {
40518             //    el = this.footer.container.insertSibling(false, 'before');
40519             //} else {
40520                 el = this.el.createChild();
40521             //}
40522
40523             this.form = new  Roo.form.Form(cfg);
40524             
40525             
40526             if ( this.form.allItems.length) {
40527                 this.form.render(el.dom);
40528             }
40529             return this.form;
40530         }
40531         // should only have one of theses..
40532         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40533             // views.. should not be just added - used named prop 'view''
40534             
40535             cfg.el = this.el.appendChild(document.createElement("div"));
40536             // factory?
40537             
40538             var ret = new Roo.factory(cfg);
40539              
40540              ret.render && ret.render(false, ''); // render blank..
40541             this.view = ret;
40542             return ret;
40543         }
40544         return false;
40545     }
40546     \*/
40547 });
40548  
40549 /**
40550  * @class Roo.bootstrap.panel.Grid
40551  * @extends Roo.bootstrap.panel.Content
40552  * @constructor
40553  * Create a new GridPanel.
40554  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40555  * @param {Object} config A the config object
40556   
40557  */
40558
40559
40560
40561 Roo.bootstrap.panel.Grid = function(config)
40562 {
40563     
40564       
40565     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40566         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40567
40568     config.el = this.wrapper;
40569     //this.el = this.wrapper;
40570     
40571       if (config.container) {
40572         // ctor'ed from a Border/panel.grid
40573         
40574         
40575         this.wrapper.setStyle("overflow", "hidden");
40576         this.wrapper.addClass('roo-grid-container');
40577
40578     }
40579     
40580     
40581     if(config.toolbar){
40582         var tool_el = this.wrapper.createChild();    
40583         this.toolbar = Roo.factory(config.toolbar);
40584         var ti = [];
40585         if (config.toolbar.items) {
40586             ti = config.toolbar.items ;
40587             delete config.toolbar.items ;
40588         }
40589         
40590         var nitems = [];
40591         this.toolbar.render(tool_el);
40592         for(var i =0;i < ti.length;i++) {
40593           //  Roo.log(['add child', items[i]]);
40594             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40595         }
40596         this.toolbar.items = nitems;
40597         
40598         delete config.toolbar;
40599     }
40600     
40601     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40602     config.grid.scrollBody = true;;
40603     config.grid.monitorWindowResize = false; // turn off autosizing
40604     config.grid.autoHeight = false;
40605     config.grid.autoWidth = false;
40606     
40607     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40608     
40609     if (config.background) {
40610         // render grid on panel activation (if panel background)
40611         this.on('activate', function(gp) {
40612             if (!gp.grid.rendered) {
40613                 gp.grid.render(this.wrapper);
40614                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40615             }
40616         });
40617             
40618     } else {
40619         this.grid.render(this.wrapper);
40620         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40621
40622     }
40623     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40624     // ??? needed ??? config.el = this.wrapper;
40625     
40626     
40627     
40628   
40629     // xtype created footer. - not sure if will work as we normally have to render first..
40630     if (this.footer && !this.footer.el && this.footer.xtype) {
40631         
40632         var ctr = this.grid.getView().getFooterPanel(true);
40633         this.footer.dataSource = this.grid.dataSource;
40634         this.footer = Roo.factory(this.footer, Roo);
40635         this.footer.render(ctr);
40636         
40637     }
40638     
40639     
40640     
40641     
40642      
40643 };
40644
40645 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40646     getId : function(){
40647         return this.grid.id;
40648     },
40649     
40650     /**
40651      * Returns the grid for this panel
40652      * @return {Roo.bootstrap.Table} 
40653      */
40654     getGrid : function(){
40655         return this.grid;    
40656     },
40657     
40658     setSize : function(width, height){
40659         if(!this.ignoreResize(width, height)){
40660             var grid = this.grid;
40661             var size = this.adjustForComponents(width, height);
40662             // tfoot is not a footer?
40663           
40664             
40665             var gridel = grid.getGridEl();
40666             gridel.setSize(size.width, size.height);
40667             
40668             var tbd = grid.getGridEl().select('tbody', true).first();
40669             var thd = grid.getGridEl().select('thead',true).first();
40670             var tbf= grid.getGridEl().select('tfoot', true).first();
40671
40672             if (tbf) {
40673                 size.height -= tbf.getHeight();
40674             }
40675             if (thd) {
40676                 size.height -= thd.getHeight();
40677             }
40678             
40679             tbd.setSize(size.width, size.height );
40680             // this is for the account management tab -seems to work there.
40681             var thd = grid.getGridEl().select('thead',true).first();
40682             //if (tbd) {
40683             //    tbd.setSize(size.width, size.height - thd.getHeight());
40684             //}
40685              
40686             grid.autoSize();
40687         }
40688     },
40689      
40690     
40691     
40692     beforeSlide : function(){
40693         this.grid.getView().scroller.clip();
40694     },
40695     
40696     afterSlide : function(){
40697         this.grid.getView().scroller.unclip();
40698     },
40699     
40700     destroy : function(){
40701         this.grid.destroy();
40702         delete this.grid;
40703         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40704     }
40705 });
40706
40707 /**
40708  * @class Roo.bootstrap.panel.Nest
40709  * @extends Roo.bootstrap.panel.Content
40710  * @constructor
40711  * Create a new Panel, that can contain a layout.Border.
40712  * 
40713  * 
40714  * @param {Roo.BorderLayout} layout The layout for this panel
40715  * @param {String/Object} config A string to set only the title or a config object
40716  */
40717 Roo.bootstrap.panel.Nest = function(config)
40718 {
40719     // construct with only one argument..
40720     /* FIXME - implement nicer consturctors
40721     if (layout.layout) {
40722         config = layout;
40723         layout = config.layout;
40724         delete config.layout;
40725     }
40726     if (layout.xtype && !layout.getEl) {
40727         // then layout needs constructing..
40728         layout = Roo.factory(layout, Roo);
40729     }
40730     */
40731     
40732     config.el =  config.layout.getEl();
40733     
40734     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40735     
40736     config.layout.monitorWindowResize = false; // turn off autosizing
40737     this.layout = config.layout;
40738     this.layout.getEl().addClass("roo-layout-nested-layout");
40739     this.layout.parent = this;
40740     
40741     
40742     
40743     
40744 };
40745
40746 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40747
40748     setSize : function(width, height){
40749         if(!this.ignoreResize(width, height)){
40750             var size = this.adjustForComponents(width, height);
40751             var el = this.layout.getEl();
40752             if (size.height < 1) {
40753                 el.setWidth(size.width);   
40754             } else {
40755                 el.setSize(size.width, size.height);
40756             }
40757             var touch = el.dom.offsetWidth;
40758             this.layout.layout();
40759             // ie requires a double layout on the first pass
40760             if(Roo.isIE && !this.initialized){
40761                 this.initialized = true;
40762                 this.layout.layout();
40763             }
40764         }
40765     },
40766     
40767     // activate all subpanels if not currently active..
40768     
40769     setActiveState : function(active){
40770         this.active = active;
40771         this.setActiveClass(active);
40772         
40773         if(!active){
40774             this.fireEvent("deactivate", this);
40775             return;
40776         }
40777         
40778         this.fireEvent("activate", this);
40779         // not sure if this should happen before or after..
40780         if (!this.layout) {
40781             return; // should not happen..
40782         }
40783         var reg = false;
40784         for (var r in this.layout.regions) {
40785             reg = this.layout.getRegion(r);
40786             if (reg.getActivePanel()) {
40787                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40788                 reg.setActivePanel(reg.getActivePanel());
40789                 continue;
40790             }
40791             if (!reg.panels.length) {
40792                 continue;
40793             }
40794             reg.showPanel(reg.getPanel(0));
40795         }
40796         
40797         
40798         
40799         
40800     },
40801     
40802     /**
40803      * Returns the nested BorderLayout for this panel
40804      * @return {Roo.BorderLayout} 
40805      */
40806     getLayout : function(){
40807         return this.layout;
40808     },
40809     
40810      /**
40811      * Adds a xtype elements to the layout of the nested panel
40812      * <pre><code>
40813
40814 panel.addxtype({
40815        xtype : 'ContentPanel',
40816        region: 'west',
40817        items: [ .... ]
40818    }
40819 );
40820
40821 panel.addxtype({
40822         xtype : 'NestedLayoutPanel',
40823         region: 'west',
40824         layout: {
40825            center: { },
40826            west: { }   
40827         },
40828         items : [ ... list of content panels or nested layout panels.. ]
40829    }
40830 );
40831 </code></pre>
40832      * @param {Object} cfg Xtype definition of item to add.
40833      */
40834     addxtype : function(cfg) {
40835         return this.layout.addxtype(cfg);
40836     
40837     }
40838 });/*
40839  * Based on:
40840  * Ext JS Library 1.1.1
40841  * Copyright(c) 2006-2007, Ext JS, LLC.
40842  *
40843  * Originally Released Under LGPL - original licence link has changed is not relivant.
40844  *
40845  * Fork - LGPL
40846  * <script type="text/javascript">
40847  */
40848 /**
40849  * @class Roo.TabPanel
40850  * @extends Roo.util.Observable
40851  * A lightweight tab container.
40852  * <br><br>
40853  * Usage:
40854  * <pre><code>
40855 // basic tabs 1, built from existing content
40856 var tabs = new Roo.TabPanel("tabs1");
40857 tabs.addTab("script", "View Script");
40858 tabs.addTab("markup", "View Markup");
40859 tabs.activate("script");
40860
40861 // more advanced tabs, built from javascript
40862 var jtabs = new Roo.TabPanel("jtabs");
40863 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40864
40865 // set up the UpdateManager
40866 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40867 var updater = tab2.getUpdateManager();
40868 updater.setDefaultUrl("ajax1.htm");
40869 tab2.on('activate', updater.refresh, updater, true);
40870
40871 // Use setUrl for Ajax loading
40872 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40873 tab3.setUrl("ajax2.htm", null, true);
40874
40875 // Disabled tab
40876 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40877 tab4.disable();
40878
40879 jtabs.activate("jtabs-1");
40880  * </code></pre>
40881  * @constructor
40882  * Create a new TabPanel.
40883  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40884  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40885  */
40886 Roo.bootstrap.panel.Tabs = function(config){
40887     /**
40888     * The container element for this TabPanel.
40889     * @type Roo.Element
40890     */
40891     this.el = Roo.get(config.el);
40892     delete config.el;
40893     if(config){
40894         if(typeof config == "boolean"){
40895             this.tabPosition = config ? "bottom" : "top";
40896         }else{
40897             Roo.apply(this, config);
40898         }
40899     }
40900     
40901     if(this.tabPosition == "bottom"){
40902         // if tabs are at the bottom = create the body first.
40903         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40904         this.el.addClass("roo-tabs-bottom");
40905     }
40906     // next create the tabs holders
40907     
40908     if (this.tabPosition == "west"){
40909         
40910         var reg = this.region; // fake it..
40911         while (reg) {
40912             if (!reg.mgr.parent) {
40913                 break;
40914             }
40915             reg = reg.mgr.parent.region;
40916         }
40917         Roo.log("got nest?");
40918         Roo.log(reg);
40919         if (reg.mgr.getRegion('west')) {
40920             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40921             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40922             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40923             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40924             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40925         
40926             
40927         }
40928         
40929         
40930     } else {
40931      
40932         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40933         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40934         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40935         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40936     }
40937     
40938     
40939     if(Roo.isIE){
40940         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40941     }
40942     
40943     // finally - if tabs are at the top, then create the body last..
40944     if(this.tabPosition != "bottom"){
40945         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40946          * @type Roo.Element
40947          */
40948         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40949         this.el.addClass("roo-tabs-top");
40950     }
40951     this.items = [];
40952
40953     this.bodyEl.setStyle("position", "relative");
40954
40955     this.active = null;
40956     this.activateDelegate = this.activate.createDelegate(this);
40957
40958     this.addEvents({
40959         /**
40960          * @event tabchange
40961          * Fires when the active tab changes
40962          * @param {Roo.TabPanel} this
40963          * @param {Roo.TabPanelItem} activePanel The new active tab
40964          */
40965         "tabchange": true,
40966         /**
40967          * @event beforetabchange
40968          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40969          * @param {Roo.TabPanel} this
40970          * @param {Object} e Set cancel to true on this object to cancel the tab change
40971          * @param {Roo.TabPanelItem} tab The tab being changed to
40972          */
40973         "beforetabchange" : true
40974     });
40975
40976     Roo.EventManager.onWindowResize(this.onResize, this);
40977     this.cpad = this.el.getPadding("lr");
40978     this.hiddenCount = 0;
40979
40980
40981     // toolbar on the tabbar support...
40982     if (this.toolbar) {
40983         alert("no toolbar support yet");
40984         this.toolbar  = false;
40985         /*
40986         var tcfg = this.toolbar;
40987         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40988         this.toolbar = new Roo.Toolbar(tcfg);
40989         if (Roo.isSafari) {
40990             var tbl = tcfg.container.child('table', true);
40991             tbl.setAttribute('width', '100%');
40992         }
40993         */
40994         
40995     }
40996    
40997
40998
40999     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41000 };
41001
41002 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41003     /*
41004      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41005      */
41006     tabPosition : "top",
41007     /*
41008      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41009      */
41010     currentTabWidth : 0,
41011     /*
41012      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41013      */
41014     minTabWidth : 40,
41015     /*
41016      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41017      */
41018     maxTabWidth : 250,
41019     /*
41020      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41021      */
41022     preferredTabWidth : 175,
41023     /*
41024      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41025      */
41026     resizeTabs : false,
41027     /*
41028      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41029      */
41030     monitorResize : true,
41031     /*
41032      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41033      */
41034     toolbar : false,  // set by caller..
41035     
41036     region : false, /// set by caller
41037     
41038     disableTooltips : true, // not used yet...
41039
41040     /**
41041      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41042      * @param {String} id The id of the div to use <b>or create</b>
41043      * @param {String} text The text for the tab
41044      * @param {String} content (optional) Content to put in the TabPanelItem body
41045      * @param {Boolean} closable (optional) True to create a close icon on the tab
41046      * @return {Roo.TabPanelItem} The created TabPanelItem
41047      */
41048     addTab : function(id, text, content, closable, tpl)
41049     {
41050         var item = new Roo.bootstrap.panel.TabItem({
41051             panel: this,
41052             id : id,
41053             text : text,
41054             closable : closable,
41055             tpl : tpl
41056         });
41057         this.addTabItem(item);
41058         if(content){
41059             item.setContent(content);
41060         }
41061         return item;
41062     },
41063
41064     /**
41065      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41066      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41067      * @return {Roo.TabPanelItem}
41068      */
41069     getTab : function(id){
41070         return this.items[id];
41071     },
41072
41073     /**
41074      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41075      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41076      */
41077     hideTab : function(id){
41078         var t = this.items[id];
41079         if(!t.isHidden()){
41080            t.setHidden(true);
41081            this.hiddenCount++;
41082            this.autoSizeTabs();
41083         }
41084     },
41085
41086     /**
41087      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41088      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41089      */
41090     unhideTab : function(id){
41091         var t = this.items[id];
41092         if(t.isHidden()){
41093            t.setHidden(false);
41094            this.hiddenCount--;
41095            this.autoSizeTabs();
41096         }
41097     },
41098
41099     /**
41100      * Adds an existing {@link Roo.TabPanelItem}.
41101      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41102      */
41103     addTabItem : function(item)
41104     {
41105         this.items[item.id] = item;
41106         this.items.push(item);
41107         this.autoSizeTabs();
41108       //  if(this.resizeTabs){
41109     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41110   //         this.autoSizeTabs();
41111 //        }else{
41112 //            item.autoSize();
41113        // }
41114     },
41115
41116     /**
41117      * Removes a {@link Roo.TabPanelItem}.
41118      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41119      */
41120     removeTab : function(id){
41121         var items = this.items;
41122         var tab = items[id];
41123         if(!tab) { return; }
41124         var index = items.indexOf(tab);
41125         if(this.active == tab && items.length > 1){
41126             var newTab = this.getNextAvailable(index);
41127             if(newTab) {
41128                 newTab.activate();
41129             }
41130         }
41131         this.stripEl.dom.removeChild(tab.pnode.dom);
41132         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41133             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41134         }
41135         items.splice(index, 1);
41136         delete this.items[tab.id];
41137         tab.fireEvent("close", tab);
41138         tab.purgeListeners();
41139         this.autoSizeTabs();
41140     },
41141
41142     getNextAvailable : function(start){
41143         var items = this.items;
41144         var index = start;
41145         // look for a next tab that will slide over to
41146         // replace the one being removed
41147         while(index < items.length){
41148             var item = items[++index];
41149             if(item && !item.isHidden()){
41150                 return item;
41151             }
41152         }
41153         // if one isn't found select the previous tab (on the left)
41154         index = start;
41155         while(index >= 0){
41156             var item = items[--index];
41157             if(item && !item.isHidden()){
41158                 return item;
41159             }
41160         }
41161         return null;
41162     },
41163
41164     /**
41165      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41166      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41167      */
41168     disableTab : function(id){
41169         var tab = this.items[id];
41170         if(tab && this.active != tab){
41171             tab.disable();
41172         }
41173     },
41174
41175     /**
41176      * Enables a {@link Roo.TabPanelItem} that is disabled.
41177      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41178      */
41179     enableTab : function(id){
41180         var tab = this.items[id];
41181         tab.enable();
41182     },
41183
41184     /**
41185      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41186      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41187      * @return {Roo.TabPanelItem} The TabPanelItem.
41188      */
41189     activate : function(id)
41190     {
41191         //Roo.log('activite:'  + id);
41192         
41193         var tab = this.items[id];
41194         if(!tab){
41195             return null;
41196         }
41197         if(tab == this.active || tab.disabled){
41198             return tab;
41199         }
41200         var e = {};
41201         this.fireEvent("beforetabchange", this, e, tab);
41202         if(e.cancel !== true && !tab.disabled){
41203             if(this.active){
41204                 this.active.hide();
41205             }
41206             this.active = this.items[id];
41207             this.active.show();
41208             this.fireEvent("tabchange", this, this.active);
41209         }
41210         return tab;
41211     },
41212
41213     /**
41214      * Gets the active {@link Roo.TabPanelItem}.
41215      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41216      */
41217     getActiveTab : function(){
41218         return this.active;
41219     },
41220
41221     /**
41222      * Updates the tab body element to fit the height of the container element
41223      * for overflow scrolling
41224      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41225      */
41226     syncHeight : function(targetHeight){
41227         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41228         var bm = this.bodyEl.getMargins();
41229         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41230         this.bodyEl.setHeight(newHeight);
41231         return newHeight;
41232     },
41233
41234     onResize : function(){
41235         if(this.monitorResize){
41236             this.autoSizeTabs();
41237         }
41238     },
41239
41240     /**
41241      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41242      */
41243     beginUpdate : function(){
41244         this.updating = true;
41245     },
41246
41247     /**
41248      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41249      */
41250     endUpdate : function(){
41251         this.updating = false;
41252         this.autoSizeTabs();
41253     },
41254
41255     /**
41256      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41257      */
41258     autoSizeTabs : function()
41259     {
41260         var count = this.items.length;
41261         var vcount = count - this.hiddenCount;
41262         
41263         if (vcount < 2) {
41264             this.stripEl.hide();
41265         } else {
41266             this.stripEl.show();
41267         }
41268         
41269         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41270             return;
41271         }
41272         
41273         
41274         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41275         var availWidth = Math.floor(w / vcount);
41276         var b = this.stripBody;
41277         if(b.getWidth() > w){
41278             var tabs = this.items;
41279             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41280             if(availWidth < this.minTabWidth){
41281                 /*if(!this.sleft){    // incomplete scrolling code
41282                     this.createScrollButtons();
41283                 }
41284                 this.showScroll();
41285                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41286             }
41287         }else{
41288             if(this.currentTabWidth < this.preferredTabWidth){
41289                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41290             }
41291         }
41292     },
41293
41294     /**
41295      * Returns the number of tabs in this TabPanel.
41296      * @return {Number}
41297      */
41298      getCount : function(){
41299          return this.items.length;
41300      },
41301
41302     /**
41303      * Resizes all the tabs to the passed width
41304      * @param {Number} The new width
41305      */
41306     setTabWidth : function(width){
41307         this.currentTabWidth = width;
41308         for(var i = 0, len = this.items.length; i < len; i++) {
41309                 if(!this.items[i].isHidden()) {
41310                 this.items[i].setWidth(width);
41311             }
41312         }
41313     },
41314
41315     /**
41316      * Destroys this TabPanel
41317      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41318      */
41319     destroy : function(removeEl){
41320         Roo.EventManager.removeResizeListener(this.onResize, this);
41321         for(var i = 0, len = this.items.length; i < len; i++){
41322             this.items[i].purgeListeners();
41323         }
41324         if(removeEl === true){
41325             this.el.update("");
41326             this.el.remove();
41327         }
41328     },
41329     
41330     createStrip : function(container)
41331     {
41332         var strip = document.createElement("nav");
41333         strip.className = Roo.bootstrap.version == 4 ?
41334             "navbar-light bg-light" : 
41335             "navbar navbar-default"; //"x-tabs-wrap";
41336         container.appendChild(strip);
41337         return strip;
41338     },
41339     
41340     createStripList : function(strip)
41341     {
41342         // div wrapper for retard IE
41343         // returns the "tr" element.
41344         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41345         //'<div class="x-tabs-strip-wrap">'+
41346           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41347           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41348         return strip.firstChild; //.firstChild.firstChild.firstChild;
41349     },
41350     createBody : function(container)
41351     {
41352         var body = document.createElement("div");
41353         Roo.id(body, "tab-body");
41354         //Roo.fly(body).addClass("x-tabs-body");
41355         Roo.fly(body).addClass("tab-content");
41356         container.appendChild(body);
41357         return body;
41358     },
41359     createItemBody :function(bodyEl, id){
41360         var body = Roo.getDom(id);
41361         if(!body){
41362             body = document.createElement("div");
41363             body.id = id;
41364         }
41365         //Roo.fly(body).addClass("x-tabs-item-body");
41366         Roo.fly(body).addClass("tab-pane");
41367          bodyEl.insertBefore(body, bodyEl.firstChild);
41368         return body;
41369     },
41370     /** @private */
41371     createStripElements :  function(stripEl, text, closable, tpl)
41372     {
41373         var td = document.createElement("li"); // was td..
41374         td.className = 'nav-item';
41375         
41376         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41377         
41378         
41379         stripEl.appendChild(td);
41380         /*if(closable){
41381             td.className = "x-tabs-closable";
41382             if(!this.closeTpl){
41383                 this.closeTpl = new Roo.Template(
41384                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41385                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41386                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41387                 );
41388             }
41389             var el = this.closeTpl.overwrite(td, {"text": text});
41390             var close = el.getElementsByTagName("div")[0];
41391             var inner = el.getElementsByTagName("em")[0];
41392             return {"el": el, "close": close, "inner": inner};
41393         } else {
41394         */
41395         // not sure what this is..
41396 //            if(!this.tabTpl){
41397                 //this.tabTpl = new Roo.Template(
41398                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41399                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41400                 //);
41401 //                this.tabTpl = new Roo.Template(
41402 //                   '<a href="#">' +
41403 //                   '<span unselectable="on"' +
41404 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41405 //                            ' >{text}</span></a>'
41406 //                );
41407 //                
41408 //            }
41409
41410
41411             var template = tpl || this.tabTpl || false;
41412             
41413             if(!template){
41414                 template =  new Roo.Template(
41415                         Roo.bootstrap.version == 4 ? 
41416                             (
41417                                 '<a class="nav-link" href="#" unselectable="on"' +
41418                                      (this.disableTooltips ? '' : ' title="{text}"') +
41419                                      ' >{text}</a>'
41420                             ) : (
41421                                 '<a class="nav-link" href="#">' +
41422                                 '<span unselectable="on"' +
41423                                          (this.disableTooltips ? '' : ' title="{text}"') +
41424                                     ' >{text}</span></a>'
41425                             )
41426                 );
41427             }
41428             
41429             switch (typeof(template)) {
41430                 case 'object' :
41431                     break;
41432                 case 'string' :
41433                     template = new Roo.Template(template);
41434                     break;
41435                 default :
41436                     break;
41437             }
41438             
41439             var el = template.overwrite(td, {"text": text});
41440             
41441             var inner = el.getElementsByTagName("span")[0];
41442             
41443             return {"el": el, "inner": inner};
41444             
41445     }
41446         
41447     
41448 });
41449
41450 /**
41451  * @class Roo.TabPanelItem
41452  * @extends Roo.util.Observable
41453  * Represents an individual item (tab plus body) in a TabPanel.
41454  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41455  * @param {String} id The id of this TabPanelItem
41456  * @param {String} text The text for the tab of this TabPanelItem
41457  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41458  */
41459 Roo.bootstrap.panel.TabItem = function(config){
41460     /**
41461      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41462      * @type Roo.TabPanel
41463      */
41464     this.tabPanel = config.panel;
41465     /**
41466      * The id for this TabPanelItem
41467      * @type String
41468      */
41469     this.id = config.id;
41470     /** @private */
41471     this.disabled = false;
41472     /** @private */
41473     this.text = config.text;
41474     /** @private */
41475     this.loaded = false;
41476     this.closable = config.closable;
41477
41478     /**
41479      * The body element for this TabPanelItem.
41480      * @type Roo.Element
41481      */
41482     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41483     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41484     this.bodyEl.setStyle("display", "block");
41485     this.bodyEl.setStyle("zoom", "1");
41486     //this.hideAction();
41487
41488     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41489     /** @private */
41490     this.el = Roo.get(els.el);
41491     this.inner = Roo.get(els.inner, true);
41492      this.textEl = Roo.bootstrap.version == 4 ?
41493         this.el : Roo.get(this.el.dom.firstChild, true);
41494
41495     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41496     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41497
41498     
41499 //    this.el.on("mousedown", this.onTabMouseDown, this);
41500     this.el.on("click", this.onTabClick, this);
41501     /** @private */
41502     if(config.closable){
41503         var c = Roo.get(els.close, true);
41504         c.dom.title = this.closeText;
41505         c.addClassOnOver("close-over");
41506         c.on("click", this.closeClick, this);
41507      }
41508
41509     this.addEvents({
41510          /**
41511          * @event activate
41512          * Fires when this tab becomes the active tab.
41513          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41514          * @param {Roo.TabPanelItem} this
41515          */
41516         "activate": true,
41517         /**
41518          * @event beforeclose
41519          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41520          * @param {Roo.TabPanelItem} this
41521          * @param {Object} e Set cancel to true on this object to cancel the close.
41522          */
41523         "beforeclose": true,
41524         /**
41525          * @event close
41526          * Fires when this tab is closed.
41527          * @param {Roo.TabPanelItem} this
41528          */
41529          "close": true,
41530         /**
41531          * @event deactivate
41532          * Fires when this tab is no longer the active tab.
41533          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41534          * @param {Roo.TabPanelItem} this
41535          */
41536          "deactivate" : true
41537     });
41538     this.hidden = false;
41539
41540     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41541 };
41542
41543 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41544            {
41545     purgeListeners : function(){
41546        Roo.util.Observable.prototype.purgeListeners.call(this);
41547        this.el.removeAllListeners();
41548     },
41549     /**
41550      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41551      */
41552     show : function(){
41553         this.status_node.addClass("active");
41554         this.showAction();
41555         if(Roo.isOpera){
41556             this.tabPanel.stripWrap.repaint();
41557         }
41558         this.fireEvent("activate", this.tabPanel, this);
41559     },
41560
41561     /**
41562      * Returns true if this tab is the active tab.
41563      * @return {Boolean}
41564      */
41565     isActive : function(){
41566         return this.tabPanel.getActiveTab() == this;
41567     },
41568
41569     /**
41570      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41571      */
41572     hide : function(){
41573         this.status_node.removeClass("active");
41574         this.hideAction();
41575         this.fireEvent("deactivate", this.tabPanel, this);
41576     },
41577
41578     hideAction : function(){
41579         this.bodyEl.hide();
41580         this.bodyEl.setStyle("position", "absolute");
41581         this.bodyEl.setLeft("-20000px");
41582         this.bodyEl.setTop("-20000px");
41583     },
41584
41585     showAction : function(){
41586         this.bodyEl.setStyle("position", "relative");
41587         this.bodyEl.setTop("");
41588         this.bodyEl.setLeft("");
41589         this.bodyEl.show();
41590     },
41591
41592     /**
41593      * Set the tooltip for the tab.
41594      * @param {String} tooltip The tab's tooltip
41595      */
41596     setTooltip : function(text){
41597         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41598             this.textEl.dom.qtip = text;
41599             this.textEl.dom.removeAttribute('title');
41600         }else{
41601             this.textEl.dom.title = text;
41602         }
41603     },
41604
41605     onTabClick : function(e){
41606         e.preventDefault();
41607         this.tabPanel.activate(this.id);
41608     },
41609
41610     onTabMouseDown : function(e){
41611         e.preventDefault();
41612         this.tabPanel.activate(this.id);
41613     },
41614 /*
41615     getWidth : function(){
41616         return this.inner.getWidth();
41617     },
41618
41619     setWidth : function(width){
41620         var iwidth = width - this.linode.getPadding("lr");
41621         this.inner.setWidth(iwidth);
41622         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41623         this.linode.setWidth(width);
41624     },
41625 */
41626     /**
41627      * Show or hide the tab
41628      * @param {Boolean} hidden True to hide or false to show.
41629      */
41630     setHidden : function(hidden){
41631         this.hidden = hidden;
41632         this.linode.setStyle("display", hidden ? "none" : "");
41633     },
41634
41635     /**
41636      * Returns true if this tab is "hidden"
41637      * @return {Boolean}
41638      */
41639     isHidden : function(){
41640         return this.hidden;
41641     },
41642
41643     /**
41644      * Returns the text for this tab
41645      * @return {String}
41646      */
41647     getText : function(){
41648         return this.text;
41649     },
41650     /*
41651     autoSize : function(){
41652         //this.el.beginMeasure();
41653         this.textEl.setWidth(1);
41654         /*
41655          *  #2804 [new] Tabs in Roojs
41656          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41657          */
41658         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41659         //this.el.endMeasure();
41660     //},
41661
41662     /**
41663      * Sets the text for the tab (Note: this also sets the tooltip text)
41664      * @param {String} text The tab's text and tooltip
41665      */
41666     setText : function(text){
41667         this.text = text;
41668         this.textEl.update(text);
41669         this.setTooltip(text);
41670         //if(!this.tabPanel.resizeTabs){
41671         //    this.autoSize();
41672         //}
41673     },
41674     /**
41675      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41676      */
41677     activate : function(){
41678         this.tabPanel.activate(this.id);
41679     },
41680
41681     /**
41682      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41683      */
41684     disable : function(){
41685         if(this.tabPanel.active != this){
41686             this.disabled = true;
41687             this.status_node.addClass("disabled");
41688         }
41689     },
41690
41691     /**
41692      * Enables this TabPanelItem if it was previously disabled.
41693      */
41694     enable : function(){
41695         this.disabled = false;
41696         this.status_node.removeClass("disabled");
41697     },
41698
41699     /**
41700      * Sets the content for this TabPanelItem.
41701      * @param {String} content The content
41702      * @param {Boolean} loadScripts true to look for and load scripts
41703      */
41704     setContent : function(content, loadScripts){
41705         this.bodyEl.update(content, loadScripts);
41706     },
41707
41708     /**
41709      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41710      * @return {Roo.UpdateManager} The UpdateManager
41711      */
41712     getUpdateManager : function(){
41713         return this.bodyEl.getUpdateManager();
41714     },
41715
41716     /**
41717      * Set a URL to be used to load the content for this TabPanelItem.
41718      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41719      * @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)
41720      * @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)
41721      * @return {Roo.UpdateManager} The UpdateManager
41722      */
41723     setUrl : function(url, params, loadOnce){
41724         if(this.refreshDelegate){
41725             this.un('activate', this.refreshDelegate);
41726         }
41727         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41728         this.on("activate", this.refreshDelegate);
41729         return this.bodyEl.getUpdateManager();
41730     },
41731
41732     /** @private */
41733     _handleRefresh : function(url, params, loadOnce){
41734         if(!loadOnce || !this.loaded){
41735             var updater = this.bodyEl.getUpdateManager();
41736             updater.update(url, params, this._setLoaded.createDelegate(this));
41737         }
41738     },
41739
41740     /**
41741      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41742      *   Will fail silently if the setUrl method has not been called.
41743      *   This does not activate the panel, just updates its content.
41744      */
41745     refresh : function(){
41746         if(this.refreshDelegate){
41747            this.loaded = false;
41748            this.refreshDelegate();
41749         }
41750     },
41751
41752     /** @private */
41753     _setLoaded : function(){
41754         this.loaded = true;
41755     },
41756
41757     /** @private */
41758     closeClick : function(e){
41759         var o = {};
41760         e.stopEvent();
41761         this.fireEvent("beforeclose", this, o);
41762         if(o.cancel !== true){
41763             this.tabPanel.removeTab(this.id);
41764         }
41765     },
41766     /**
41767      * The text displayed in the tooltip for the close icon.
41768      * @type String
41769      */
41770     closeText : "Close this tab"
41771 });
41772 /**
41773 *    This script refer to:
41774 *    Title: International Telephone Input
41775 *    Author: Jack O'Connor
41776 *    Code version:  v12.1.12
41777 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41778 **/
41779
41780 Roo.bootstrap.PhoneInputData = function() {
41781     var d = [
41782       [
41783         "Afghanistan (‫افغانستان‬‎)",
41784         "af",
41785         "93"
41786       ],
41787       [
41788         "Albania (Shqipëri)",
41789         "al",
41790         "355"
41791       ],
41792       [
41793         "Algeria (‫الجزائر‬‎)",
41794         "dz",
41795         "213"
41796       ],
41797       [
41798         "American Samoa",
41799         "as",
41800         "1684"
41801       ],
41802       [
41803         "Andorra",
41804         "ad",
41805         "376"
41806       ],
41807       [
41808         "Angola",
41809         "ao",
41810         "244"
41811       ],
41812       [
41813         "Anguilla",
41814         "ai",
41815         "1264"
41816       ],
41817       [
41818         "Antigua and Barbuda",
41819         "ag",
41820         "1268"
41821       ],
41822       [
41823         "Argentina",
41824         "ar",
41825         "54"
41826       ],
41827       [
41828         "Armenia (Հայաստան)",
41829         "am",
41830         "374"
41831       ],
41832       [
41833         "Aruba",
41834         "aw",
41835         "297"
41836       ],
41837       [
41838         "Australia",
41839         "au",
41840         "61",
41841         0
41842       ],
41843       [
41844         "Austria (Österreich)",
41845         "at",
41846         "43"
41847       ],
41848       [
41849         "Azerbaijan (Azərbaycan)",
41850         "az",
41851         "994"
41852       ],
41853       [
41854         "Bahamas",
41855         "bs",
41856         "1242"
41857       ],
41858       [
41859         "Bahrain (‫البحرين‬‎)",
41860         "bh",
41861         "973"
41862       ],
41863       [
41864         "Bangladesh (বাংলাদেশ)",
41865         "bd",
41866         "880"
41867       ],
41868       [
41869         "Barbados",
41870         "bb",
41871         "1246"
41872       ],
41873       [
41874         "Belarus (Беларусь)",
41875         "by",
41876         "375"
41877       ],
41878       [
41879         "Belgium (België)",
41880         "be",
41881         "32"
41882       ],
41883       [
41884         "Belize",
41885         "bz",
41886         "501"
41887       ],
41888       [
41889         "Benin (Bénin)",
41890         "bj",
41891         "229"
41892       ],
41893       [
41894         "Bermuda",
41895         "bm",
41896         "1441"
41897       ],
41898       [
41899         "Bhutan (འབྲུག)",
41900         "bt",
41901         "975"
41902       ],
41903       [
41904         "Bolivia",
41905         "bo",
41906         "591"
41907       ],
41908       [
41909         "Bosnia and Herzegovina (Босна и Херцеговина)",
41910         "ba",
41911         "387"
41912       ],
41913       [
41914         "Botswana",
41915         "bw",
41916         "267"
41917       ],
41918       [
41919         "Brazil (Brasil)",
41920         "br",
41921         "55"
41922       ],
41923       [
41924         "British Indian Ocean Territory",
41925         "io",
41926         "246"
41927       ],
41928       [
41929         "British Virgin Islands",
41930         "vg",
41931         "1284"
41932       ],
41933       [
41934         "Brunei",
41935         "bn",
41936         "673"
41937       ],
41938       [
41939         "Bulgaria (България)",
41940         "bg",
41941         "359"
41942       ],
41943       [
41944         "Burkina Faso",
41945         "bf",
41946         "226"
41947       ],
41948       [
41949         "Burundi (Uburundi)",
41950         "bi",
41951         "257"
41952       ],
41953       [
41954         "Cambodia (កម្ពុជា)",
41955         "kh",
41956         "855"
41957       ],
41958       [
41959         "Cameroon (Cameroun)",
41960         "cm",
41961         "237"
41962       ],
41963       [
41964         "Canada",
41965         "ca",
41966         "1",
41967         1,
41968         ["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"]
41969       ],
41970       [
41971         "Cape Verde (Kabu Verdi)",
41972         "cv",
41973         "238"
41974       ],
41975       [
41976         "Caribbean Netherlands",
41977         "bq",
41978         "599",
41979         1
41980       ],
41981       [
41982         "Cayman Islands",
41983         "ky",
41984         "1345"
41985       ],
41986       [
41987         "Central African Republic (République centrafricaine)",
41988         "cf",
41989         "236"
41990       ],
41991       [
41992         "Chad (Tchad)",
41993         "td",
41994         "235"
41995       ],
41996       [
41997         "Chile",
41998         "cl",
41999         "56"
42000       ],
42001       [
42002         "China (中国)",
42003         "cn",
42004         "86"
42005       ],
42006       [
42007         "Christmas Island",
42008         "cx",
42009         "61",
42010         2
42011       ],
42012       [
42013         "Cocos (Keeling) Islands",
42014         "cc",
42015         "61",
42016         1
42017       ],
42018       [
42019         "Colombia",
42020         "co",
42021         "57"
42022       ],
42023       [
42024         "Comoros (‫جزر القمر‬‎)",
42025         "km",
42026         "269"
42027       ],
42028       [
42029         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42030         "cd",
42031         "243"
42032       ],
42033       [
42034         "Congo (Republic) (Congo-Brazzaville)",
42035         "cg",
42036         "242"
42037       ],
42038       [
42039         "Cook Islands",
42040         "ck",
42041         "682"
42042       ],
42043       [
42044         "Costa Rica",
42045         "cr",
42046         "506"
42047       ],
42048       [
42049         "Côte d’Ivoire",
42050         "ci",
42051         "225"
42052       ],
42053       [
42054         "Croatia (Hrvatska)",
42055         "hr",
42056         "385"
42057       ],
42058       [
42059         "Cuba",
42060         "cu",
42061         "53"
42062       ],
42063       [
42064         "Curaçao",
42065         "cw",
42066         "599",
42067         0
42068       ],
42069       [
42070         "Cyprus (Κύπρος)",
42071         "cy",
42072         "357"
42073       ],
42074       [
42075         "Czech Republic (Česká republika)",
42076         "cz",
42077         "420"
42078       ],
42079       [
42080         "Denmark (Danmark)",
42081         "dk",
42082         "45"
42083       ],
42084       [
42085         "Djibouti",
42086         "dj",
42087         "253"
42088       ],
42089       [
42090         "Dominica",
42091         "dm",
42092         "1767"
42093       ],
42094       [
42095         "Dominican Republic (República Dominicana)",
42096         "do",
42097         "1",
42098         2,
42099         ["809", "829", "849"]
42100       ],
42101       [
42102         "Ecuador",
42103         "ec",
42104         "593"
42105       ],
42106       [
42107         "Egypt (‫مصر‬‎)",
42108         "eg",
42109         "20"
42110       ],
42111       [
42112         "El Salvador",
42113         "sv",
42114         "503"
42115       ],
42116       [
42117         "Equatorial Guinea (Guinea Ecuatorial)",
42118         "gq",
42119         "240"
42120       ],
42121       [
42122         "Eritrea",
42123         "er",
42124         "291"
42125       ],
42126       [
42127         "Estonia (Eesti)",
42128         "ee",
42129         "372"
42130       ],
42131       [
42132         "Ethiopia",
42133         "et",
42134         "251"
42135       ],
42136       [
42137         "Falkland Islands (Islas Malvinas)",
42138         "fk",
42139         "500"
42140       ],
42141       [
42142         "Faroe Islands (Føroyar)",
42143         "fo",
42144         "298"
42145       ],
42146       [
42147         "Fiji",
42148         "fj",
42149         "679"
42150       ],
42151       [
42152         "Finland (Suomi)",
42153         "fi",
42154         "358",
42155         0
42156       ],
42157       [
42158         "France",
42159         "fr",
42160         "33"
42161       ],
42162       [
42163         "French Guiana (Guyane française)",
42164         "gf",
42165         "594"
42166       ],
42167       [
42168         "French Polynesia (Polynésie française)",
42169         "pf",
42170         "689"
42171       ],
42172       [
42173         "Gabon",
42174         "ga",
42175         "241"
42176       ],
42177       [
42178         "Gambia",
42179         "gm",
42180         "220"
42181       ],
42182       [
42183         "Georgia (საქართველო)",
42184         "ge",
42185         "995"
42186       ],
42187       [
42188         "Germany (Deutschland)",
42189         "de",
42190         "49"
42191       ],
42192       [
42193         "Ghana (Gaana)",
42194         "gh",
42195         "233"
42196       ],
42197       [
42198         "Gibraltar",
42199         "gi",
42200         "350"
42201       ],
42202       [
42203         "Greece (Ελλάδα)",
42204         "gr",
42205         "30"
42206       ],
42207       [
42208         "Greenland (Kalaallit Nunaat)",
42209         "gl",
42210         "299"
42211       ],
42212       [
42213         "Grenada",
42214         "gd",
42215         "1473"
42216       ],
42217       [
42218         "Guadeloupe",
42219         "gp",
42220         "590",
42221         0
42222       ],
42223       [
42224         "Guam",
42225         "gu",
42226         "1671"
42227       ],
42228       [
42229         "Guatemala",
42230         "gt",
42231         "502"
42232       ],
42233       [
42234         "Guernsey",
42235         "gg",
42236         "44",
42237         1
42238       ],
42239       [
42240         "Guinea (Guinée)",
42241         "gn",
42242         "224"
42243       ],
42244       [
42245         "Guinea-Bissau (Guiné Bissau)",
42246         "gw",
42247         "245"
42248       ],
42249       [
42250         "Guyana",
42251         "gy",
42252         "592"
42253       ],
42254       [
42255         "Haiti",
42256         "ht",
42257         "509"
42258       ],
42259       [
42260         "Honduras",
42261         "hn",
42262         "504"
42263       ],
42264       [
42265         "Hong Kong (香港)",
42266         "hk",
42267         "852"
42268       ],
42269       [
42270         "Hungary (Magyarország)",
42271         "hu",
42272         "36"
42273       ],
42274       [
42275         "Iceland (Ísland)",
42276         "is",
42277         "354"
42278       ],
42279       [
42280         "India (भारत)",
42281         "in",
42282         "91"
42283       ],
42284       [
42285         "Indonesia",
42286         "id",
42287         "62"
42288       ],
42289       [
42290         "Iran (‫ایران‬‎)",
42291         "ir",
42292         "98"
42293       ],
42294       [
42295         "Iraq (‫العراق‬‎)",
42296         "iq",
42297         "964"
42298       ],
42299       [
42300         "Ireland",
42301         "ie",
42302         "353"
42303       ],
42304       [
42305         "Isle of Man",
42306         "im",
42307         "44",
42308         2
42309       ],
42310       [
42311         "Israel (‫ישראל‬‎)",
42312         "il",
42313         "972"
42314       ],
42315       [
42316         "Italy (Italia)",
42317         "it",
42318         "39",
42319         0
42320       ],
42321       [
42322         "Jamaica",
42323         "jm",
42324         "1876"
42325       ],
42326       [
42327         "Japan (日本)",
42328         "jp",
42329         "81"
42330       ],
42331       [
42332         "Jersey",
42333         "je",
42334         "44",
42335         3
42336       ],
42337       [
42338         "Jordan (‫الأردن‬‎)",
42339         "jo",
42340         "962"
42341       ],
42342       [
42343         "Kazakhstan (Казахстан)",
42344         "kz",
42345         "7",
42346         1
42347       ],
42348       [
42349         "Kenya",
42350         "ke",
42351         "254"
42352       ],
42353       [
42354         "Kiribati",
42355         "ki",
42356         "686"
42357       ],
42358       [
42359         "Kosovo",
42360         "xk",
42361         "383"
42362       ],
42363       [
42364         "Kuwait (‫الكويت‬‎)",
42365         "kw",
42366         "965"
42367       ],
42368       [
42369         "Kyrgyzstan (Кыргызстан)",
42370         "kg",
42371         "996"
42372       ],
42373       [
42374         "Laos (ລາວ)",
42375         "la",
42376         "856"
42377       ],
42378       [
42379         "Latvia (Latvija)",
42380         "lv",
42381         "371"
42382       ],
42383       [
42384         "Lebanon (‫لبنان‬‎)",
42385         "lb",
42386         "961"
42387       ],
42388       [
42389         "Lesotho",
42390         "ls",
42391         "266"
42392       ],
42393       [
42394         "Liberia",
42395         "lr",
42396         "231"
42397       ],
42398       [
42399         "Libya (‫ليبيا‬‎)",
42400         "ly",
42401         "218"
42402       ],
42403       [
42404         "Liechtenstein",
42405         "li",
42406         "423"
42407       ],
42408       [
42409         "Lithuania (Lietuva)",
42410         "lt",
42411         "370"
42412       ],
42413       [
42414         "Luxembourg",
42415         "lu",
42416         "352"
42417       ],
42418       [
42419         "Macau (澳門)",
42420         "mo",
42421         "853"
42422       ],
42423       [
42424         "Macedonia (FYROM) (Македонија)",
42425         "mk",
42426         "389"
42427       ],
42428       [
42429         "Madagascar (Madagasikara)",
42430         "mg",
42431         "261"
42432       ],
42433       [
42434         "Malawi",
42435         "mw",
42436         "265"
42437       ],
42438       [
42439         "Malaysia",
42440         "my",
42441         "60"
42442       ],
42443       [
42444         "Maldives",
42445         "mv",
42446         "960"
42447       ],
42448       [
42449         "Mali",
42450         "ml",
42451         "223"
42452       ],
42453       [
42454         "Malta",
42455         "mt",
42456         "356"
42457       ],
42458       [
42459         "Marshall Islands",
42460         "mh",
42461         "692"
42462       ],
42463       [
42464         "Martinique",
42465         "mq",
42466         "596"
42467       ],
42468       [
42469         "Mauritania (‫موريتانيا‬‎)",
42470         "mr",
42471         "222"
42472       ],
42473       [
42474         "Mauritius (Moris)",
42475         "mu",
42476         "230"
42477       ],
42478       [
42479         "Mayotte",
42480         "yt",
42481         "262",
42482         1
42483       ],
42484       [
42485         "Mexico (México)",
42486         "mx",
42487         "52"
42488       ],
42489       [
42490         "Micronesia",
42491         "fm",
42492         "691"
42493       ],
42494       [
42495         "Moldova (Republica Moldova)",
42496         "md",
42497         "373"
42498       ],
42499       [
42500         "Monaco",
42501         "mc",
42502         "377"
42503       ],
42504       [
42505         "Mongolia (Монгол)",
42506         "mn",
42507         "976"
42508       ],
42509       [
42510         "Montenegro (Crna Gora)",
42511         "me",
42512         "382"
42513       ],
42514       [
42515         "Montserrat",
42516         "ms",
42517         "1664"
42518       ],
42519       [
42520         "Morocco (‫المغرب‬‎)",
42521         "ma",
42522         "212",
42523         0
42524       ],
42525       [
42526         "Mozambique (Moçambique)",
42527         "mz",
42528         "258"
42529       ],
42530       [
42531         "Myanmar (Burma) (မြန်မာ)",
42532         "mm",
42533         "95"
42534       ],
42535       [
42536         "Namibia (Namibië)",
42537         "na",
42538         "264"
42539       ],
42540       [
42541         "Nauru",
42542         "nr",
42543         "674"
42544       ],
42545       [
42546         "Nepal (नेपाल)",
42547         "np",
42548         "977"
42549       ],
42550       [
42551         "Netherlands (Nederland)",
42552         "nl",
42553         "31"
42554       ],
42555       [
42556         "New Caledonia (Nouvelle-Calédonie)",
42557         "nc",
42558         "687"
42559       ],
42560       [
42561         "New Zealand",
42562         "nz",
42563         "64"
42564       ],
42565       [
42566         "Nicaragua",
42567         "ni",
42568         "505"
42569       ],
42570       [
42571         "Niger (Nijar)",
42572         "ne",
42573         "227"
42574       ],
42575       [
42576         "Nigeria",
42577         "ng",
42578         "234"
42579       ],
42580       [
42581         "Niue",
42582         "nu",
42583         "683"
42584       ],
42585       [
42586         "Norfolk Island",
42587         "nf",
42588         "672"
42589       ],
42590       [
42591         "North Korea (조선 민주주의 인민 공화국)",
42592         "kp",
42593         "850"
42594       ],
42595       [
42596         "Northern Mariana Islands",
42597         "mp",
42598         "1670"
42599       ],
42600       [
42601         "Norway (Norge)",
42602         "no",
42603         "47",
42604         0
42605       ],
42606       [
42607         "Oman (‫عُمان‬‎)",
42608         "om",
42609         "968"
42610       ],
42611       [
42612         "Pakistan (‫پاکستان‬‎)",
42613         "pk",
42614         "92"
42615       ],
42616       [
42617         "Palau",
42618         "pw",
42619         "680"
42620       ],
42621       [
42622         "Palestine (‫فلسطين‬‎)",
42623         "ps",
42624         "970"
42625       ],
42626       [
42627         "Panama (Panamá)",
42628         "pa",
42629         "507"
42630       ],
42631       [
42632         "Papua New Guinea",
42633         "pg",
42634         "675"
42635       ],
42636       [
42637         "Paraguay",
42638         "py",
42639         "595"
42640       ],
42641       [
42642         "Peru (Perú)",
42643         "pe",
42644         "51"
42645       ],
42646       [
42647         "Philippines",
42648         "ph",
42649         "63"
42650       ],
42651       [
42652         "Poland (Polska)",
42653         "pl",
42654         "48"
42655       ],
42656       [
42657         "Portugal",
42658         "pt",
42659         "351"
42660       ],
42661       [
42662         "Puerto Rico",
42663         "pr",
42664         "1",
42665         3,
42666         ["787", "939"]
42667       ],
42668       [
42669         "Qatar (‫قطر‬‎)",
42670         "qa",
42671         "974"
42672       ],
42673       [
42674         "Réunion (La Réunion)",
42675         "re",
42676         "262",
42677         0
42678       ],
42679       [
42680         "Romania (România)",
42681         "ro",
42682         "40"
42683       ],
42684       [
42685         "Russia (Россия)",
42686         "ru",
42687         "7",
42688         0
42689       ],
42690       [
42691         "Rwanda",
42692         "rw",
42693         "250"
42694       ],
42695       [
42696         "Saint Barthélemy",
42697         "bl",
42698         "590",
42699         1
42700       ],
42701       [
42702         "Saint Helena",
42703         "sh",
42704         "290"
42705       ],
42706       [
42707         "Saint Kitts and Nevis",
42708         "kn",
42709         "1869"
42710       ],
42711       [
42712         "Saint Lucia",
42713         "lc",
42714         "1758"
42715       ],
42716       [
42717         "Saint Martin (Saint-Martin (partie française))",
42718         "mf",
42719         "590",
42720         2
42721       ],
42722       [
42723         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42724         "pm",
42725         "508"
42726       ],
42727       [
42728         "Saint Vincent and the Grenadines",
42729         "vc",
42730         "1784"
42731       ],
42732       [
42733         "Samoa",
42734         "ws",
42735         "685"
42736       ],
42737       [
42738         "San Marino",
42739         "sm",
42740         "378"
42741       ],
42742       [
42743         "São Tomé and Príncipe (São Tomé e Príncipe)",
42744         "st",
42745         "239"
42746       ],
42747       [
42748         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42749         "sa",
42750         "966"
42751       ],
42752       [
42753         "Senegal (Sénégal)",
42754         "sn",
42755         "221"
42756       ],
42757       [
42758         "Serbia (Србија)",
42759         "rs",
42760         "381"
42761       ],
42762       [
42763         "Seychelles",
42764         "sc",
42765         "248"
42766       ],
42767       [
42768         "Sierra Leone",
42769         "sl",
42770         "232"
42771       ],
42772       [
42773         "Singapore",
42774         "sg",
42775         "65"
42776       ],
42777       [
42778         "Sint Maarten",
42779         "sx",
42780         "1721"
42781       ],
42782       [
42783         "Slovakia (Slovensko)",
42784         "sk",
42785         "421"
42786       ],
42787       [
42788         "Slovenia (Slovenija)",
42789         "si",
42790         "386"
42791       ],
42792       [
42793         "Solomon Islands",
42794         "sb",
42795         "677"
42796       ],
42797       [
42798         "Somalia (Soomaaliya)",
42799         "so",
42800         "252"
42801       ],
42802       [
42803         "South Africa",
42804         "za",
42805         "27"
42806       ],
42807       [
42808         "South Korea (대한민국)",
42809         "kr",
42810         "82"
42811       ],
42812       [
42813         "South Sudan (‫جنوب السودان‬‎)",
42814         "ss",
42815         "211"
42816       ],
42817       [
42818         "Spain (España)",
42819         "es",
42820         "34"
42821       ],
42822       [
42823         "Sri Lanka (ශ්‍රී ලංකාව)",
42824         "lk",
42825         "94"
42826       ],
42827       [
42828         "Sudan (‫السودان‬‎)",
42829         "sd",
42830         "249"
42831       ],
42832       [
42833         "Suriname",
42834         "sr",
42835         "597"
42836       ],
42837       [
42838         "Svalbard and Jan Mayen",
42839         "sj",
42840         "47",
42841         1
42842       ],
42843       [
42844         "Swaziland",
42845         "sz",
42846         "268"
42847       ],
42848       [
42849         "Sweden (Sverige)",
42850         "se",
42851         "46"
42852       ],
42853       [
42854         "Switzerland (Schweiz)",
42855         "ch",
42856         "41"
42857       ],
42858       [
42859         "Syria (‫سوريا‬‎)",
42860         "sy",
42861         "963"
42862       ],
42863       [
42864         "Taiwan (台灣)",
42865         "tw",
42866         "886"
42867       ],
42868       [
42869         "Tajikistan",
42870         "tj",
42871         "992"
42872       ],
42873       [
42874         "Tanzania",
42875         "tz",
42876         "255"
42877       ],
42878       [
42879         "Thailand (ไทย)",
42880         "th",
42881         "66"
42882       ],
42883       [
42884         "Timor-Leste",
42885         "tl",
42886         "670"
42887       ],
42888       [
42889         "Togo",
42890         "tg",
42891         "228"
42892       ],
42893       [
42894         "Tokelau",
42895         "tk",
42896         "690"
42897       ],
42898       [
42899         "Tonga",
42900         "to",
42901         "676"
42902       ],
42903       [
42904         "Trinidad and Tobago",
42905         "tt",
42906         "1868"
42907       ],
42908       [
42909         "Tunisia (‫تونس‬‎)",
42910         "tn",
42911         "216"
42912       ],
42913       [
42914         "Turkey (Türkiye)",
42915         "tr",
42916         "90"
42917       ],
42918       [
42919         "Turkmenistan",
42920         "tm",
42921         "993"
42922       ],
42923       [
42924         "Turks and Caicos Islands",
42925         "tc",
42926         "1649"
42927       ],
42928       [
42929         "Tuvalu",
42930         "tv",
42931         "688"
42932       ],
42933       [
42934         "U.S. Virgin Islands",
42935         "vi",
42936         "1340"
42937       ],
42938       [
42939         "Uganda",
42940         "ug",
42941         "256"
42942       ],
42943       [
42944         "Ukraine (Україна)",
42945         "ua",
42946         "380"
42947       ],
42948       [
42949         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42950         "ae",
42951         "971"
42952       ],
42953       [
42954         "United Kingdom",
42955         "gb",
42956         "44",
42957         0
42958       ],
42959       [
42960         "United States",
42961         "us",
42962         "1",
42963         0
42964       ],
42965       [
42966         "Uruguay",
42967         "uy",
42968         "598"
42969       ],
42970       [
42971         "Uzbekistan (Oʻzbekiston)",
42972         "uz",
42973         "998"
42974       ],
42975       [
42976         "Vanuatu",
42977         "vu",
42978         "678"
42979       ],
42980       [
42981         "Vatican City (Città del Vaticano)",
42982         "va",
42983         "39",
42984         1
42985       ],
42986       [
42987         "Venezuela",
42988         "ve",
42989         "58"
42990       ],
42991       [
42992         "Vietnam (Việt Nam)",
42993         "vn",
42994         "84"
42995       ],
42996       [
42997         "Wallis and Futuna (Wallis-et-Futuna)",
42998         "wf",
42999         "681"
43000       ],
43001       [
43002         "Western Sahara (‫الصحراء الغربية‬‎)",
43003         "eh",
43004         "212",
43005         1
43006       ],
43007       [
43008         "Yemen (‫اليمن‬‎)",
43009         "ye",
43010         "967"
43011       ],
43012       [
43013         "Zambia",
43014         "zm",
43015         "260"
43016       ],
43017       [
43018         "Zimbabwe",
43019         "zw",
43020         "263"
43021       ],
43022       [
43023         "Åland Islands",
43024         "ax",
43025         "358",
43026         1
43027       ]
43028   ];
43029   
43030   return d;
43031 }/**
43032 *    This script refer to:
43033 *    Title: International Telephone Input
43034 *    Author: Jack O'Connor
43035 *    Code version:  v12.1.12
43036 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43037 **/
43038
43039 /**
43040  * @class Roo.bootstrap.PhoneInput
43041  * @extends Roo.bootstrap.TriggerField
43042  * An input with International dial-code selection
43043  
43044  * @cfg {String} defaultDialCode default '+852'
43045  * @cfg {Array} preferedCountries default []
43046   
43047  * @constructor
43048  * Create a new PhoneInput.
43049  * @param {Object} config Configuration options
43050  */
43051
43052 Roo.bootstrap.PhoneInput = function(config) {
43053     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43054 };
43055
43056 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43057         
43058         listWidth: undefined,
43059         
43060         selectedClass: 'active',
43061         
43062         invalidClass : "has-warning",
43063         
43064         validClass: 'has-success',
43065         
43066         allowed: '0123456789',
43067         
43068         max_length: 15,
43069         
43070         /**
43071          * @cfg {String} defaultDialCode The default dial code when initializing the input
43072          */
43073         defaultDialCode: '+852',
43074         
43075         /**
43076          * @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
43077          */
43078         preferedCountries: false,
43079         
43080         getAutoCreate : function()
43081         {
43082             var data = Roo.bootstrap.PhoneInputData();
43083             var align = this.labelAlign || this.parentLabelAlign();
43084             var id = Roo.id();
43085             
43086             this.allCountries = [];
43087             this.dialCodeMapping = [];
43088             
43089             for (var i = 0; i < data.length; i++) {
43090               var c = data[i];
43091               this.allCountries[i] = {
43092                 name: c[0],
43093                 iso2: c[1],
43094                 dialCode: c[2],
43095                 priority: c[3] || 0,
43096                 areaCodes: c[4] || null
43097               };
43098               this.dialCodeMapping[c[2]] = {
43099                   name: c[0],
43100                   iso2: c[1],
43101                   priority: c[3] || 0,
43102                   areaCodes: c[4] || null
43103               };
43104             }
43105             
43106             var cfg = {
43107                 cls: 'form-group',
43108                 cn: []
43109             };
43110             
43111             var input =  {
43112                 tag: 'input',
43113                 id : id,
43114                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43115                 maxlength: this.max_length,
43116                 cls : 'form-control tel-input',
43117                 autocomplete: 'new-password'
43118             };
43119             
43120             var hiddenInput = {
43121                 tag: 'input',
43122                 type: 'hidden',
43123                 cls: 'hidden-tel-input'
43124             };
43125             
43126             if (this.name) {
43127                 hiddenInput.name = this.name;
43128             }
43129             
43130             if (this.disabled) {
43131                 input.disabled = true;
43132             }
43133             
43134             var flag_container = {
43135                 tag: 'div',
43136                 cls: 'flag-box',
43137                 cn: [
43138                     {
43139                         tag: 'div',
43140                         cls: 'flag'
43141                     },
43142                     {
43143                         tag: 'div',
43144                         cls: 'caret'
43145                     }
43146                 ]
43147             };
43148             
43149             var box = {
43150                 tag: 'div',
43151                 cls: this.hasFeedback ? 'has-feedback' : '',
43152                 cn: [
43153                     hiddenInput,
43154                     input,
43155                     {
43156                         tag: 'input',
43157                         cls: 'dial-code-holder',
43158                         disabled: true
43159                     }
43160                 ]
43161             };
43162             
43163             var container = {
43164                 cls: 'roo-select2-container input-group',
43165                 cn: [
43166                     flag_container,
43167                     box
43168                 ]
43169             };
43170             
43171             if (this.fieldLabel.length) {
43172                 var indicator = {
43173                     tag: 'i',
43174                     tooltip: 'This field is required'
43175                 };
43176                 
43177                 var label = {
43178                     tag: 'label',
43179                     'for':  id,
43180                     cls: 'control-label',
43181                     cn: []
43182                 };
43183                 
43184                 var label_text = {
43185                     tag: 'span',
43186                     html: this.fieldLabel
43187                 };
43188                 
43189                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43190                 label.cn = [
43191                     indicator,
43192                     label_text
43193                 ];
43194                 
43195                 if(this.indicatorpos == 'right') {
43196                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43197                     label.cn = [
43198                         label_text,
43199                         indicator
43200                     ];
43201                 }
43202                 
43203                 if(align == 'left') {
43204                     container = {
43205                         tag: 'div',
43206                         cn: [
43207                             container
43208                         ]
43209                     };
43210                     
43211                     if(this.labelWidth > 12){
43212                         label.style = "width: " + this.labelWidth + 'px';
43213                     }
43214                     if(this.labelWidth < 13 && this.labelmd == 0){
43215                         this.labelmd = this.labelWidth;
43216                     }
43217                     if(this.labellg > 0){
43218                         label.cls += ' col-lg-' + this.labellg;
43219                         input.cls += ' col-lg-' + (12 - this.labellg);
43220                     }
43221                     if(this.labelmd > 0){
43222                         label.cls += ' col-md-' + this.labelmd;
43223                         container.cls += ' col-md-' + (12 - this.labelmd);
43224                     }
43225                     if(this.labelsm > 0){
43226                         label.cls += ' col-sm-' + this.labelsm;
43227                         container.cls += ' col-sm-' + (12 - this.labelsm);
43228                     }
43229                     if(this.labelxs > 0){
43230                         label.cls += ' col-xs-' + this.labelxs;
43231                         container.cls += ' col-xs-' + (12 - this.labelxs);
43232                     }
43233                 }
43234             }
43235             
43236             cfg.cn = [
43237                 label,
43238                 container
43239             ];
43240             
43241             var settings = this;
43242             
43243             ['xs','sm','md','lg'].map(function(size){
43244                 if (settings[size]) {
43245                     cfg.cls += ' col-' + size + '-' + settings[size];
43246                 }
43247             });
43248             
43249             this.store = new Roo.data.Store({
43250                 proxy : new Roo.data.MemoryProxy({}),
43251                 reader : new Roo.data.JsonReader({
43252                     fields : [
43253                         {
43254                             'name' : 'name',
43255                             'type' : 'string'
43256                         },
43257                         {
43258                             'name' : 'iso2',
43259                             'type' : 'string'
43260                         },
43261                         {
43262                             'name' : 'dialCode',
43263                             'type' : 'string'
43264                         },
43265                         {
43266                             'name' : 'priority',
43267                             'type' : 'string'
43268                         },
43269                         {
43270                             'name' : 'areaCodes',
43271                             'type' : 'string'
43272                         }
43273                     ]
43274                 })
43275             });
43276             
43277             if(!this.preferedCountries) {
43278                 this.preferedCountries = [
43279                     'hk',
43280                     'gb',
43281                     'us'
43282                 ];
43283             }
43284             
43285             var p = this.preferedCountries.reverse();
43286             
43287             if(p) {
43288                 for (var i = 0; i < p.length; i++) {
43289                     for (var j = 0; j < this.allCountries.length; j++) {
43290                         if(this.allCountries[j].iso2 == p[i]) {
43291                             var t = this.allCountries[j];
43292                             this.allCountries.splice(j,1);
43293                             this.allCountries.unshift(t);
43294                         }
43295                     } 
43296                 }
43297             }
43298             
43299             this.store.proxy.data = {
43300                 success: true,
43301                 data: this.allCountries
43302             };
43303             
43304             return cfg;
43305         },
43306         
43307         initEvents : function()
43308         {
43309             this.createList();
43310             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43311             
43312             this.indicator = this.indicatorEl();
43313             this.flag = this.flagEl();
43314             this.dialCodeHolder = this.dialCodeHolderEl();
43315             
43316             this.trigger = this.el.select('div.flag-box',true).first();
43317             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43318             
43319             var _this = this;
43320             
43321             (function(){
43322                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43323                 _this.list.setWidth(lw);
43324             }).defer(100);
43325             
43326             this.list.on('mouseover', this.onViewOver, this);
43327             this.list.on('mousemove', this.onViewMove, this);
43328             this.inputEl().on("keyup", this.onKeyUp, this);
43329             this.inputEl().on("keypress", this.onKeyPress, this);
43330             
43331             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43332
43333             this.view = new Roo.View(this.list, this.tpl, {
43334                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43335             });
43336             
43337             this.view.on('click', this.onViewClick, this);
43338             this.setValue(this.defaultDialCode);
43339         },
43340         
43341         onTriggerClick : function(e)
43342         {
43343             Roo.log('trigger click');
43344             if(this.disabled){
43345                 return;
43346             }
43347             
43348             if(this.isExpanded()){
43349                 this.collapse();
43350                 this.hasFocus = false;
43351             }else {
43352                 this.store.load({});
43353                 this.hasFocus = true;
43354                 this.expand();
43355             }
43356         },
43357         
43358         isExpanded : function()
43359         {
43360             return this.list.isVisible();
43361         },
43362         
43363         collapse : function()
43364         {
43365             if(!this.isExpanded()){
43366                 return;
43367             }
43368             this.list.hide();
43369             Roo.get(document).un('mousedown', this.collapseIf, this);
43370             Roo.get(document).un('mousewheel', this.collapseIf, this);
43371             this.fireEvent('collapse', this);
43372             this.validate();
43373         },
43374         
43375         expand : function()
43376         {
43377             Roo.log('expand');
43378
43379             if(this.isExpanded() || !this.hasFocus){
43380                 return;
43381             }
43382             
43383             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43384             this.list.setWidth(lw);
43385             
43386             this.list.show();
43387             this.restrictHeight();
43388             
43389             Roo.get(document).on('mousedown', this.collapseIf, this);
43390             Roo.get(document).on('mousewheel', this.collapseIf, this);
43391             
43392             this.fireEvent('expand', this);
43393         },
43394         
43395         restrictHeight : function()
43396         {
43397             this.list.alignTo(this.inputEl(), this.listAlign);
43398             this.list.alignTo(this.inputEl(), this.listAlign);
43399         },
43400         
43401         onViewOver : function(e, t)
43402         {
43403             if(this.inKeyMode){
43404                 return;
43405             }
43406             var item = this.view.findItemFromChild(t);
43407             
43408             if(item){
43409                 var index = this.view.indexOf(item);
43410                 this.select(index, false);
43411             }
43412         },
43413
43414         // private
43415         onViewClick : function(view, doFocus, el, e)
43416         {
43417             var index = this.view.getSelectedIndexes()[0];
43418             
43419             var r = this.store.getAt(index);
43420             
43421             if(r){
43422                 this.onSelect(r, index);
43423             }
43424             if(doFocus !== false && !this.blockFocus){
43425                 this.inputEl().focus();
43426             }
43427         },
43428         
43429         onViewMove : function(e, t)
43430         {
43431             this.inKeyMode = false;
43432         },
43433         
43434         select : function(index, scrollIntoView)
43435         {
43436             this.selectedIndex = index;
43437             this.view.select(index);
43438             if(scrollIntoView !== false){
43439                 var el = this.view.getNode(index);
43440                 if(el){
43441                     this.list.scrollChildIntoView(el, false);
43442                 }
43443             }
43444         },
43445         
43446         createList : function()
43447         {
43448             this.list = Roo.get(document.body).createChild({
43449                 tag: 'ul',
43450                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43451                 style: 'display:none'
43452             });
43453             
43454             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43455         },
43456         
43457         collapseIf : function(e)
43458         {
43459             var in_combo  = e.within(this.el);
43460             var in_list =  e.within(this.list);
43461             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43462             
43463             if (in_combo || in_list || is_list) {
43464                 return;
43465             }
43466             this.collapse();
43467         },
43468         
43469         onSelect : function(record, index)
43470         {
43471             if(this.fireEvent('beforeselect', this, record, index) !== false){
43472                 
43473                 this.setFlagClass(record.data.iso2);
43474                 this.setDialCode(record.data.dialCode);
43475                 this.hasFocus = false;
43476                 this.collapse();
43477                 this.fireEvent('select', this, record, index);
43478             }
43479         },
43480         
43481         flagEl : function()
43482         {
43483             var flag = this.el.select('div.flag',true).first();
43484             if(!flag){
43485                 return false;
43486             }
43487             return flag;
43488         },
43489         
43490         dialCodeHolderEl : function()
43491         {
43492             var d = this.el.select('input.dial-code-holder',true).first();
43493             if(!d){
43494                 return false;
43495             }
43496             return d;
43497         },
43498         
43499         setDialCode : function(v)
43500         {
43501             this.dialCodeHolder.dom.value = '+'+v;
43502         },
43503         
43504         setFlagClass : function(n)
43505         {
43506             this.flag.dom.className = 'flag '+n;
43507         },
43508         
43509         getValue : function()
43510         {
43511             var v = this.inputEl().getValue();
43512             if(this.dialCodeHolder) {
43513                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43514             }
43515             return v;
43516         },
43517         
43518         setValue : function(v)
43519         {
43520             var d = this.getDialCode(v);
43521             
43522             //invalid dial code
43523             if(v.length == 0 || !d || d.length == 0) {
43524                 if(this.rendered){
43525                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43526                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43527                 }
43528                 return;
43529             }
43530             
43531             //valid dial code
43532             this.setFlagClass(this.dialCodeMapping[d].iso2);
43533             this.setDialCode(d);
43534             this.inputEl().dom.value = v.replace('+'+d,'');
43535             this.hiddenEl().dom.value = this.getValue();
43536             
43537             this.validate();
43538         },
43539         
43540         getDialCode : function(v)
43541         {
43542             v = v ||  '';
43543             
43544             if (v.length == 0) {
43545                 return this.dialCodeHolder.dom.value;
43546             }
43547             
43548             var dialCode = "";
43549             if (v.charAt(0) != "+") {
43550                 return false;
43551             }
43552             var numericChars = "";
43553             for (var i = 1; i < v.length; i++) {
43554               var c = v.charAt(i);
43555               if (!isNaN(c)) {
43556                 numericChars += c;
43557                 if (this.dialCodeMapping[numericChars]) {
43558                   dialCode = v.substr(1, i);
43559                 }
43560                 if (numericChars.length == 4) {
43561                   break;
43562                 }
43563               }
43564             }
43565             return dialCode;
43566         },
43567         
43568         reset : function()
43569         {
43570             this.setValue(this.defaultDialCode);
43571             this.validate();
43572         },
43573         
43574         hiddenEl : function()
43575         {
43576             return this.el.select('input.hidden-tel-input',true).first();
43577         },
43578         
43579         // after setting val
43580         onKeyUp : function(e){
43581             this.setValue(this.getValue());
43582         },
43583         
43584         onKeyPress : function(e){
43585             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43586                 e.stopEvent();
43587             }
43588         }
43589         
43590 });
43591 /**
43592  * @class Roo.bootstrap.MoneyField
43593  * @extends Roo.bootstrap.ComboBox
43594  * Bootstrap MoneyField class
43595  * 
43596  * @constructor
43597  * Create a new MoneyField.
43598  * @param {Object} config Configuration options
43599  */
43600
43601 Roo.bootstrap.MoneyField = function(config) {
43602     
43603     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43604     
43605 };
43606
43607 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43608     
43609     /**
43610      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43611      */
43612     allowDecimals : true,
43613     /**
43614      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43615      */
43616     decimalSeparator : ".",
43617     /**
43618      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43619      */
43620     decimalPrecision : 0,
43621     /**
43622      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43623      */
43624     allowNegative : true,
43625     /**
43626      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43627      */
43628     allowZero: true,
43629     /**
43630      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43631      */
43632     minValue : Number.NEGATIVE_INFINITY,
43633     /**
43634      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43635      */
43636     maxValue : Number.MAX_VALUE,
43637     /**
43638      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43639      */
43640     minText : "The minimum value for this field is {0}",
43641     /**
43642      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43643      */
43644     maxText : "The maximum value for this field is {0}",
43645     /**
43646      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43647      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43648      */
43649     nanText : "{0} is not a valid number",
43650     /**
43651      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43652      */
43653     castInt : true,
43654     /**
43655      * @cfg {String} defaults currency of the MoneyField
43656      * value should be in lkey
43657      */
43658     defaultCurrency : false,
43659     /**
43660      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43661      */
43662     thousandsDelimiter : false,
43663     /**
43664      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43665      */
43666     max_length: false,
43667     
43668     inputlg : 9,
43669     inputmd : 9,
43670     inputsm : 9,
43671     inputxs : 6,
43672     
43673     store : false,
43674     
43675     getAutoCreate : function()
43676     {
43677         var align = this.labelAlign || this.parentLabelAlign();
43678         
43679         var id = Roo.id();
43680
43681         var cfg = {
43682             cls: 'form-group',
43683             cn: []
43684         };
43685
43686         var input =  {
43687             tag: 'input',
43688             id : id,
43689             cls : 'form-control roo-money-amount-input',
43690             autocomplete: 'new-password'
43691         };
43692         
43693         var hiddenInput = {
43694             tag: 'input',
43695             type: 'hidden',
43696             id: Roo.id(),
43697             cls: 'hidden-number-input'
43698         };
43699         
43700         if(this.max_length) {
43701             input.maxlength = this.max_length; 
43702         }
43703         
43704         if (this.name) {
43705             hiddenInput.name = this.name;
43706         }
43707
43708         if (this.disabled) {
43709             input.disabled = true;
43710         }
43711
43712         var clg = 12 - this.inputlg;
43713         var cmd = 12 - this.inputmd;
43714         var csm = 12 - this.inputsm;
43715         var cxs = 12 - this.inputxs;
43716         
43717         var container = {
43718             tag : 'div',
43719             cls : 'row roo-money-field',
43720             cn : [
43721                 {
43722                     tag : 'div',
43723                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43724                     cn : [
43725                         {
43726                             tag : 'div',
43727                             cls: 'roo-select2-container input-group',
43728                             cn: [
43729                                 {
43730                                     tag : 'input',
43731                                     cls : 'form-control roo-money-currency-input',
43732                                     autocomplete: 'new-password',
43733                                     readOnly : 1,
43734                                     name : this.currencyName
43735                                 },
43736                                 {
43737                                     tag :'span',
43738                                     cls : 'input-group-addon',
43739                                     cn : [
43740                                         {
43741                                             tag: 'span',
43742                                             cls: 'caret'
43743                                         }
43744                                     ]
43745                                 }
43746                             ]
43747                         }
43748                     ]
43749                 },
43750                 {
43751                     tag : 'div',
43752                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43753                     cn : [
43754                         {
43755                             tag: 'div',
43756                             cls: this.hasFeedback ? 'has-feedback' : '',
43757                             cn: [
43758                                 input
43759                             ]
43760                         }
43761                     ]
43762                 }
43763             ]
43764             
43765         };
43766         
43767         if (this.fieldLabel.length) {
43768             var indicator = {
43769                 tag: 'i',
43770                 tooltip: 'This field is required'
43771             };
43772
43773             var label = {
43774                 tag: 'label',
43775                 'for':  id,
43776                 cls: 'control-label',
43777                 cn: []
43778             };
43779
43780             var label_text = {
43781                 tag: 'span',
43782                 html: this.fieldLabel
43783             };
43784
43785             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43786             label.cn = [
43787                 indicator,
43788                 label_text
43789             ];
43790
43791             if(this.indicatorpos == 'right') {
43792                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43793                 label.cn = [
43794                     label_text,
43795                     indicator
43796                 ];
43797             }
43798
43799             if(align == 'left') {
43800                 container = {
43801                     tag: 'div',
43802                     cn: [
43803                         container
43804                     ]
43805                 };
43806
43807                 if(this.labelWidth > 12){
43808                     label.style = "width: " + this.labelWidth + 'px';
43809                 }
43810                 if(this.labelWidth < 13 && this.labelmd == 0){
43811                     this.labelmd = this.labelWidth;
43812                 }
43813                 if(this.labellg > 0){
43814                     label.cls += ' col-lg-' + this.labellg;
43815                     input.cls += ' col-lg-' + (12 - this.labellg);
43816                 }
43817                 if(this.labelmd > 0){
43818                     label.cls += ' col-md-' + this.labelmd;
43819                     container.cls += ' col-md-' + (12 - this.labelmd);
43820                 }
43821                 if(this.labelsm > 0){
43822                     label.cls += ' col-sm-' + this.labelsm;
43823                     container.cls += ' col-sm-' + (12 - this.labelsm);
43824                 }
43825                 if(this.labelxs > 0){
43826                     label.cls += ' col-xs-' + this.labelxs;
43827                     container.cls += ' col-xs-' + (12 - this.labelxs);
43828                 }
43829             }
43830         }
43831
43832         cfg.cn = [
43833             label,
43834             container,
43835             hiddenInput
43836         ];
43837         
43838         var settings = this;
43839
43840         ['xs','sm','md','lg'].map(function(size){
43841             if (settings[size]) {
43842                 cfg.cls += ' col-' + size + '-' + settings[size];
43843             }
43844         });
43845         
43846         return cfg;
43847     },
43848     
43849     initEvents : function()
43850     {
43851         this.indicator = this.indicatorEl();
43852         
43853         this.initCurrencyEvent();
43854         
43855         this.initNumberEvent();
43856     },
43857     
43858     initCurrencyEvent : function()
43859     {
43860         if (!this.store) {
43861             throw "can not find store for combo";
43862         }
43863         
43864         this.store = Roo.factory(this.store, Roo.data);
43865         this.store.parent = this;
43866         
43867         this.createList();
43868         
43869         this.triggerEl = this.el.select('.input-group-addon', true).first();
43870         
43871         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43872         
43873         var _this = this;
43874         
43875         (function(){
43876             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43877             _this.list.setWidth(lw);
43878         }).defer(100);
43879         
43880         this.list.on('mouseover', this.onViewOver, this);
43881         this.list.on('mousemove', this.onViewMove, this);
43882         this.list.on('scroll', this.onViewScroll, this);
43883         
43884         if(!this.tpl){
43885             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43886         }
43887         
43888         this.view = new Roo.View(this.list, this.tpl, {
43889             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43890         });
43891         
43892         this.view.on('click', this.onViewClick, this);
43893         
43894         this.store.on('beforeload', this.onBeforeLoad, this);
43895         this.store.on('load', this.onLoad, this);
43896         this.store.on('loadexception', this.onLoadException, this);
43897         
43898         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43899             "up" : function(e){
43900                 this.inKeyMode = true;
43901                 this.selectPrev();
43902             },
43903
43904             "down" : function(e){
43905                 if(!this.isExpanded()){
43906                     this.onTriggerClick();
43907                 }else{
43908                     this.inKeyMode = true;
43909                     this.selectNext();
43910                 }
43911             },
43912
43913             "enter" : function(e){
43914                 this.collapse();
43915                 
43916                 if(this.fireEvent("specialkey", this, e)){
43917                     this.onViewClick(false);
43918                 }
43919                 
43920                 return true;
43921             },
43922
43923             "esc" : function(e){
43924                 this.collapse();
43925             },
43926
43927             "tab" : function(e){
43928                 this.collapse();
43929                 
43930                 if(this.fireEvent("specialkey", this, e)){
43931                     this.onViewClick(false);
43932                 }
43933                 
43934                 return true;
43935             },
43936
43937             scope : this,
43938
43939             doRelay : function(foo, bar, hname){
43940                 if(hname == 'down' || this.scope.isExpanded()){
43941                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43942                 }
43943                 return true;
43944             },
43945
43946             forceKeyDown: true
43947         });
43948         
43949         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43950         
43951     },
43952     
43953     initNumberEvent : function(e)
43954     {
43955         this.inputEl().on("keydown" , this.fireKey,  this);
43956         this.inputEl().on("focus", this.onFocus,  this);
43957         this.inputEl().on("blur", this.onBlur,  this);
43958         
43959         this.inputEl().relayEvent('keyup', this);
43960         
43961         if(this.indicator){
43962             this.indicator.addClass('invisible');
43963         }
43964  
43965         this.originalValue = this.getValue();
43966         
43967         if(this.validationEvent == 'keyup'){
43968             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43969             this.inputEl().on('keyup', this.filterValidation, this);
43970         }
43971         else if(this.validationEvent !== false){
43972             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43973         }
43974         
43975         if(this.selectOnFocus){
43976             this.on("focus", this.preFocus, this);
43977             
43978         }
43979         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43980             this.inputEl().on("keypress", this.filterKeys, this);
43981         } else {
43982             this.inputEl().relayEvent('keypress', this);
43983         }
43984         
43985         var allowed = "0123456789";
43986         
43987         if(this.allowDecimals){
43988             allowed += this.decimalSeparator;
43989         }
43990         
43991         if(this.allowNegative){
43992             allowed += "-";
43993         }
43994         
43995         if(this.thousandsDelimiter) {
43996             allowed += ",";
43997         }
43998         
43999         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44000         
44001         var keyPress = function(e){
44002             
44003             var k = e.getKey();
44004             
44005             var c = e.getCharCode();
44006             
44007             if(
44008                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44009                     allowed.indexOf(String.fromCharCode(c)) === -1
44010             ){
44011                 e.stopEvent();
44012                 return;
44013             }
44014             
44015             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44016                 return;
44017             }
44018             
44019             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44020                 e.stopEvent();
44021             }
44022         };
44023         
44024         this.inputEl().on("keypress", keyPress, this);
44025         
44026     },
44027     
44028     onTriggerClick : function(e)
44029     {   
44030         if(this.disabled){
44031             return;
44032         }
44033         
44034         this.page = 0;
44035         this.loadNext = false;
44036         
44037         if(this.isExpanded()){
44038             this.collapse();
44039             return;
44040         }
44041         
44042         this.hasFocus = true;
44043         
44044         if(this.triggerAction == 'all') {
44045             this.doQuery(this.allQuery, true);
44046             return;
44047         }
44048         
44049         this.doQuery(this.getRawValue());
44050     },
44051     
44052     getCurrency : function()
44053     {   
44054         var v = this.currencyEl().getValue();
44055         
44056         return v;
44057     },
44058     
44059     restrictHeight : function()
44060     {
44061         this.list.alignTo(this.currencyEl(), this.listAlign);
44062         this.list.alignTo(this.currencyEl(), this.listAlign);
44063     },
44064     
44065     onViewClick : function(view, doFocus, el, e)
44066     {
44067         var index = this.view.getSelectedIndexes()[0];
44068         
44069         var r = this.store.getAt(index);
44070         
44071         if(r){
44072             this.onSelect(r, index);
44073         }
44074     },
44075     
44076     onSelect : function(record, index){
44077         
44078         if(this.fireEvent('beforeselect', this, record, index) !== false){
44079         
44080             this.setFromCurrencyData(index > -1 ? record.data : false);
44081             
44082             this.collapse();
44083             
44084             this.fireEvent('select', this, record, index);
44085         }
44086     },
44087     
44088     setFromCurrencyData : function(o)
44089     {
44090         var currency = '';
44091         
44092         this.lastCurrency = o;
44093         
44094         if (this.currencyField) {
44095             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44096         } else {
44097             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44098         }
44099         
44100         this.lastSelectionText = currency;
44101         
44102         //setting default currency
44103         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44104             this.setCurrency(this.defaultCurrency);
44105             return;
44106         }
44107         
44108         this.setCurrency(currency);
44109     },
44110     
44111     setFromData : function(o)
44112     {
44113         var c = {};
44114         
44115         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44116         
44117         this.setFromCurrencyData(c);
44118         
44119         var value = '';
44120         
44121         if (this.name) {
44122             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44123         } else {
44124             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44125         }
44126         
44127         this.setValue(value);
44128         
44129     },
44130     
44131     setCurrency : function(v)
44132     {   
44133         this.currencyValue = v;
44134         
44135         if(this.rendered){
44136             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44137             this.validate();
44138         }
44139     },
44140     
44141     setValue : function(v)
44142     {
44143         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44144         
44145         this.value = v;
44146         
44147         if(this.rendered){
44148             
44149             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44150             
44151             this.inputEl().dom.value = (v == '') ? '' :
44152                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44153             
44154             if(!this.allowZero && v === '0') {
44155                 this.hiddenEl().dom.value = '';
44156                 this.inputEl().dom.value = '';
44157             }
44158             
44159             this.validate();
44160         }
44161     },
44162     
44163     getRawValue : function()
44164     {
44165         var v = this.inputEl().getValue();
44166         
44167         return v;
44168     },
44169     
44170     getValue : function()
44171     {
44172         return this.fixPrecision(this.parseValue(this.getRawValue()));
44173     },
44174     
44175     parseValue : function(value)
44176     {
44177         if(this.thousandsDelimiter) {
44178             value += "";
44179             r = new RegExp(",", "g");
44180             value = value.replace(r, "");
44181         }
44182         
44183         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44184         return isNaN(value) ? '' : value;
44185         
44186     },
44187     
44188     fixPrecision : function(value)
44189     {
44190         if(this.thousandsDelimiter) {
44191             value += "";
44192             r = new RegExp(",", "g");
44193             value = value.replace(r, "");
44194         }
44195         
44196         var nan = isNaN(value);
44197         
44198         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44199             return nan ? '' : value;
44200         }
44201         return parseFloat(value).toFixed(this.decimalPrecision);
44202     },
44203     
44204     decimalPrecisionFcn : function(v)
44205     {
44206         return Math.floor(v);
44207     },
44208     
44209     validateValue : function(value)
44210     {
44211         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44212             return false;
44213         }
44214         
44215         var num = this.parseValue(value);
44216         
44217         if(isNaN(num)){
44218             this.markInvalid(String.format(this.nanText, value));
44219             return false;
44220         }
44221         
44222         if(num < this.minValue){
44223             this.markInvalid(String.format(this.minText, this.minValue));
44224             return false;
44225         }
44226         
44227         if(num > this.maxValue){
44228             this.markInvalid(String.format(this.maxText, this.maxValue));
44229             return false;
44230         }
44231         
44232         return true;
44233     },
44234     
44235     validate : function()
44236     {
44237         if(this.disabled || this.allowBlank){
44238             this.markValid();
44239             return true;
44240         }
44241         
44242         var currency = this.getCurrency();
44243         
44244         if(this.validateValue(this.getRawValue()) && currency.length){
44245             this.markValid();
44246             return true;
44247         }
44248         
44249         this.markInvalid();
44250         return false;
44251     },
44252     
44253     getName: function()
44254     {
44255         return this.name;
44256     },
44257     
44258     beforeBlur : function()
44259     {
44260         if(!this.castInt){
44261             return;
44262         }
44263         
44264         var v = this.parseValue(this.getRawValue());
44265         
44266         if(v || v == 0){
44267             this.setValue(v);
44268         }
44269     },
44270     
44271     onBlur : function()
44272     {
44273         this.beforeBlur();
44274         
44275         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44276             //this.el.removeClass(this.focusClass);
44277         }
44278         
44279         this.hasFocus = false;
44280         
44281         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44282             this.validate();
44283         }
44284         
44285         var v = this.getValue();
44286         
44287         if(String(v) !== String(this.startValue)){
44288             this.fireEvent('change', this, v, this.startValue);
44289         }
44290         
44291         this.fireEvent("blur", this);
44292     },
44293     
44294     inputEl : function()
44295     {
44296         return this.el.select('.roo-money-amount-input', true).first();
44297     },
44298     
44299     currencyEl : function()
44300     {
44301         return this.el.select('.roo-money-currency-input', true).first();
44302     },
44303     
44304     hiddenEl : function()
44305     {
44306         return this.el.select('input.hidden-number-input',true).first();
44307     }
44308     
44309 });/**
44310  * @class Roo.bootstrap.BezierSignature
44311  * @extends Roo.bootstrap.Component
44312  * Bootstrap BezierSignature class
44313  * This script refer to:
44314  *    Title: Signature Pad
44315  *    Author: szimek
44316  *    Availability: https://github.com/szimek/signature_pad
44317  *
44318  * @constructor
44319  * Create a new BezierSignature
44320  * @param {Object} config The config object
44321  */
44322
44323 Roo.bootstrap.BezierSignature = function(config){
44324     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44325     this.addEvents({
44326         "resize" : true
44327     });
44328 };
44329
44330 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44331 {
44332      
44333     curve_data: [],
44334     
44335     is_empty: true,
44336     
44337     mouse_btn_down: true,
44338     
44339     /**
44340      * @cfg {int} canvas height
44341      */
44342     canvas_height: '200px',
44343     
44344     /**
44345      * @cfg {float|function} Radius of a single dot.
44346      */ 
44347     dot_size: false,
44348     
44349     /**
44350      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44351      */
44352     min_width: 0.5,
44353     
44354     /**
44355      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44356      */
44357     max_width: 2.5,
44358     
44359     /**
44360      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44361      */
44362     throttle: 16,
44363     
44364     /**
44365      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44366      */
44367     min_distance: 5,
44368     
44369     /**
44370      * @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.
44371      */
44372     bg_color: 'rgba(0, 0, 0, 0)',
44373     
44374     /**
44375      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44376      */
44377     dot_color: 'black',
44378     
44379     /**
44380      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44381      */ 
44382     velocity_filter_weight: 0.7,
44383     
44384     /**
44385      * @cfg {function} Callback when stroke begin. 
44386      */
44387     onBegin: false,
44388     
44389     /**
44390      * @cfg {function} Callback when stroke end.
44391      */
44392     onEnd: false,
44393     
44394     getAutoCreate : function()
44395     {
44396         var cls = 'roo-signature column';
44397         
44398         if(this.cls){
44399             cls += ' ' + this.cls;
44400         }
44401         
44402         var col_sizes = [
44403             'lg',
44404             'md',
44405             'sm',
44406             'xs'
44407         ];
44408         
44409         for(var i = 0; i < col_sizes.length; i++) {
44410             if(this[col_sizes[i]]) {
44411                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44412             }
44413         }
44414         
44415         var cfg = {
44416             tag: 'div',
44417             cls: cls,
44418             cn: [
44419                 {
44420                     tag: 'div',
44421                     cls: 'roo-signature-body',
44422                     cn: [
44423                         {
44424                             tag: 'canvas',
44425                             cls: 'roo-signature-body-canvas',
44426                             height: this.canvas_height,
44427                             width: this.canvas_width
44428                         }
44429                     ]
44430                 },
44431                 {
44432                     tag: 'input',
44433                     type: 'file',
44434                     style: 'display: none'
44435                 }
44436             ]
44437         };
44438         
44439         return cfg;
44440     },
44441     
44442     initEvents: function() 
44443     {
44444         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44445         
44446         var canvas = this.canvasEl();
44447         
44448         // mouse && touch event swapping...
44449         canvas.dom.style.touchAction = 'none';
44450         canvas.dom.style.msTouchAction = 'none';
44451         
44452         this.mouse_btn_down = false;
44453         canvas.on('mousedown', this._handleMouseDown, this);
44454         canvas.on('mousemove', this._handleMouseMove, this);
44455         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44456         
44457         if (window.PointerEvent) {
44458             canvas.on('pointerdown', this._handleMouseDown, this);
44459             canvas.on('pointermove', this._handleMouseMove, this);
44460             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44461         }
44462         
44463         if ('ontouchstart' in window) {
44464             canvas.on('touchstart', this._handleTouchStart, this);
44465             canvas.on('touchmove', this._handleTouchMove, this);
44466             canvas.on('touchend', this._handleTouchEnd, this);
44467         }
44468         
44469         Roo.EventManager.onWindowResize(this.resize, this, true);
44470         
44471         // file input event
44472         this.fileEl().on('change', this.uploadImage, this);
44473         
44474         this.clear();
44475         
44476         this.resize();
44477     },
44478     
44479     resize: function(){
44480         
44481         var canvas = this.canvasEl().dom;
44482         var ctx = this.canvasElCtx();
44483         var img_data = false;
44484         
44485         if(canvas.width > 0) {
44486             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44487         }
44488         // setting canvas width will clean img data
44489         canvas.width = 0;
44490         
44491         var style = window.getComputedStyle ? 
44492             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44493             
44494         var padding_left = parseInt(style.paddingLeft) || 0;
44495         var padding_right = parseInt(style.paddingRight) || 0;
44496         
44497         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44498         
44499         if(img_data) {
44500             ctx.putImageData(img_data, 0, 0);
44501         }
44502     },
44503     
44504     _handleMouseDown: function(e)
44505     {
44506         if (e.browserEvent.which === 1) {
44507             this.mouse_btn_down = true;
44508             this.strokeBegin(e);
44509         }
44510     },
44511     
44512     _handleMouseMove: function (e)
44513     {
44514         if (this.mouse_btn_down) {
44515             this.strokeMoveUpdate(e);
44516         }
44517     },
44518     
44519     _handleMouseUp: function (e)
44520     {
44521         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44522             this.mouse_btn_down = false;
44523             this.strokeEnd(e);
44524         }
44525     },
44526     
44527     _handleTouchStart: function (e) {
44528         
44529         e.preventDefault();
44530         if (e.browserEvent.targetTouches.length === 1) {
44531             // var touch = e.browserEvent.changedTouches[0];
44532             // this.strokeBegin(touch);
44533             
44534              this.strokeBegin(e); // assume e catching the correct xy...
44535         }
44536     },
44537     
44538     _handleTouchMove: function (e) {
44539         e.preventDefault();
44540         // var touch = event.targetTouches[0];
44541         // _this._strokeMoveUpdate(touch);
44542         this.strokeMoveUpdate(e);
44543     },
44544     
44545     _handleTouchEnd: function (e) {
44546         var wasCanvasTouched = e.target === this.canvasEl().dom;
44547         if (wasCanvasTouched) {
44548             e.preventDefault();
44549             // var touch = event.changedTouches[0];
44550             // _this._strokeEnd(touch);
44551             this.strokeEnd(e);
44552         }
44553     },
44554     
44555     reset: function () {
44556         this._lastPoints = [];
44557         this._lastVelocity = 0;
44558         this._lastWidth = (this.min_width + this.max_width) / 2;
44559         this.canvasElCtx().fillStyle = this.dot_color;
44560     },
44561     
44562     strokeMoveUpdate: function(e)
44563     {
44564         this.strokeUpdate(e);
44565         
44566         if (this.throttle) {
44567             this.throttleStroke(this.strokeUpdate, this.throttle);
44568         }
44569         else {
44570             this.strokeUpdate(e);
44571         }
44572     },
44573     
44574     strokeBegin: function(e)
44575     {
44576         var newPointGroup = {
44577             color: this.dot_color,
44578             points: []
44579         };
44580         
44581         if (typeof this.onBegin === 'function') {
44582             this.onBegin(e);
44583         }
44584         
44585         this.curve_data.push(newPointGroup);
44586         this.reset();
44587         this.strokeUpdate(e);
44588     },
44589     
44590     strokeUpdate: function(e)
44591     {
44592         var rect = this.canvasEl().dom.getBoundingClientRect();
44593         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44594         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44595         var lastPoints = lastPointGroup.points;
44596         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44597         var isLastPointTooClose = lastPoint
44598             ? point.distanceTo(lastPoint) <= this.min_distance
44599             : false;
44600         var color = lastPointGroup.color;
44601         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44602             var curve = this.addPoint(point);
44603             if (!lastPoint) {
44604                 this.drawDot({color: color, point: point});
44605             }
44606             else if (curve) {
44607                 this.drawCurve({color: color, curve: curve});
44608             }
44609             lastPoints.push({
44610                 time: point.time,
44611                 x: point.x,
44612                 y: point.y
44613             });
44614         }
44615     },
44616     
44617     strokeEnd: function(e)
44618     {
44619         this.strokeUpdate(e);
44620         if (typeof this.onEnd === 'function') {
44621             this.onEnd(e);
44622         }
44623     },
44624     
44625     addPoint:  function (point) {
44626         var _lastPoints = this._lastPoints;
44627         _lastPoints.push(point);
44628         if (_lastPoints.length > 2) {
44629             if (_lastPoints.length === 3) {
44630                 _lastPoints.unshift(_lastPoints[0]);
44631             }
44632             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44633             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44634             _lastPoints.shift();
44635             return curve;
44636         }
44637         return null;
44638     },
44639     
44640     calculateCurveWidths: function (startPoint, endPoint) {
44641         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44642             (1 - this.velocity_filter_weight) * this._lastVelocity;
44643
44644         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44645         var widths = {
44646             end: newWidth,
44647             start: this._lastWidth
44648         };
44649         
44650         this._lastVelocity = velocity;
44651         this._lastWidth = newWidth;
44652         return widths;
44653     },
44654     
44655     drawDot: function (_a) {
44656         var color = _a.color, point = _a.point;
44657         var ctx = this.canvasElCtx();
44658         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44659         ctx.beginPath();
44660         this.drawCurveSegment(point.x, point.y, width);
44661         ctx.closePath();
44662         ctx.fillStyle = color;
44663         ctx.fill();
44664     },
44665     
44666     drawCurve: function (_a) {
44667         var color = _a.color, curve = _a.curve;
44668         var ctx = this.canvasElCtx();
44669         var widthDelta = curve.endWidth - curve.startWidth;
44670         var drawSteps = Math.floor(curve.length()) * 2;
44671         ctx.beginPath();
44672         ctx.fillStyle = color;
44673         for (var i = 0; i < drawSteps; i += 1) {
44674         var t = i / drawSteps;
44675         var tt = t * t;
44676         var ttt = tt * t;
44677         var u = 1 - t;
44678         var uu = u * u;
44679         var uuu = uu * u;
44680         var x = uuu * curve.startPoint.x;
44681         x += 3 * uu * t * curve.control1.x;
44682         x += 3 * u * tt * curve.control2.x;
44683         x += ttt * curve.endPoint.x;
44684         var y = uuu * curve.startPoint.y;
44685         y += 3 * uu * t * curve.control1.y;
44686         y += 3 * u * tt * curve.control2.y;
44687         y += ttt * curve.endPoint.y;
44688         var width = curve.startWidth + ttt * widthDelta;
44689         this.drawCurveSegment(x, y, width);
44690         }
44691         ctx.closePath();
44692         ctx.fill();
44693     },
44694     
44695     drawCurveSegment: function (x, y, width) {
44696         var ctx = this.canvasElCtx();
44697         ctx.moveTo(x, y);
44698         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44699         this.is_empty = false;
44700     },
44701     
44702     clear: function()
44703     {
44704         var ctx = this.canvasElCtx();
44705         var canvas = this.canvasEl().dom;
44706         ctx.fillStyle = this.bg_color;
44707         ctx.clearRect(0, 0, canvas.width, canvas.height);
44708         ctx.fillRect(0, 0, canvas.width, canvas.height);
44709         this.curve_data = [];
44710         this.reset();
44711         this.is_empty = true;
44712     },
44713     
44714     fileEl: function()
44715     {
44716         return  this.el.select('input',true).first();
44717     },
44718     
44719     canvasEl: function()
44720     {
44721         return this.el.select('canvas',true).first();
44722     },
44723     
44724     canvasElCtx: function()
44725     {
44726         return this.el.select('canvas',true).first().dom.getContext('2d');
44727     },
44728     
44729     getImage: function(type)
44730     {
44731         if(this.is_empty) {
44732             return false;
44733         }
44734         
44735         // encryption ?
44736         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44737     },
44738     
44739     drawFromImage: function(img_src)
44740     {
44741         var img = new Image();
44742         
44743         img.onload = function(){
44744             this.canvasElCtx().drawImage(img, 0, 0);
44745         }.bind(this);
44746         
44747         img.src = img_src;
44748         
44749         this.is_empty = false;
44750     },
44751     
44752     selectImage: function()
44753     {
44754         this.fileEl().dom.click();
44755     },
44756     
44757     uploadImage: function(e)
44758     {
44759         var reader = new FileReader();
44760         
44761         reader.onload = function(e){
44762             var img = new Image();
44763             img.onload = function(){
44764                 this.reset();
44765                 this.canvasElCtx().drawImage(img, 0, 0);
44766             }.bind(this);
44767             img.src = e.target.result;
44768         }.bind(this);
44769         
44770         reader.readAsDataURL(e.target.files[0]);
44771     },
44772     
44773     // Bezier Point Constructor
44774     Point: (function () {
44775         function Point(x, y, time) {
44776             this.x = x;
44777             this.y = y;
44778             this.time = time || Date.now();
44779         }
44780         Point.prototype.distanceTo = function (start) {
44781             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44782         };
44783         Point.prototype.equals = function (other) {
44784             return this.x === other.x && this.y === other.y && this.time === other.time;
44785         };
44786         Point.prototype.velocityFrom = function (start) {
44787             return this.time !== start.time
44788             ? this.distanceTo(start) / (this.time - start.time)
44789             : 0;
44790         };
44791         return Point;
44792     }()),
44793     
44794     
44795     // Bezier Constructor
44796     Bezier: (function () {
44797         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44798             this.startPoint = startPoint;
44799             this.control2 = control2;
44800             this.control1 = control1;
44801             this.endPoint = endPoint;
44802             this.startWidth = startWidth;
44803             this.endWidth = endWidth;
44804         }
44805         Bezier.fromPoints = function (points, widths, scope) {
44806             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44807             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44808             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44809         };
44810         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44811             var dx1 = s1.x - s2.x;
44812             var dy1 = s1.y - s2.y;
44813             var dx2 = s2.x - s3.x;
44814             var dy2 = s2.y - s3.y;
44815             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44816             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44817             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44818             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44819             var dxm = m1.x - m2.x;
44820             var dym = m1.y - m2.y;
44821             var k = l2 / (l1 + l2);
44822             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44823             var tx = s2.x - cm.x;
44824             var ty = s2.y - cm.y;
44825             return {
44826                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44827                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44828             };
44829         };
44830         Bezier.prototype.length = function () {
44831             var steps = 10;
44832             var length = 0;
44833             var px;
44834             var py;
44835             for (var i = 0; i <= steps; i += 1) {
44836                 var t = i / steps;
44837                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44838                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44839                 if (i > 0) {
44840                     var xdiff = cx - px;
44841                     var ydiff = cy - py;
44842                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44843                 }
44844                 px = cx;
44845                 py = cy;
44846             }
44847             return length;
44848         };
44849         Bezier.prototype.point = function (t, start, c1, c2, end) {
44850             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44851             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44852             + (3.0 * c2 * (1.0 - t) * t * t)
44853             + (end * t * t * t);
44854         };
44855         return Bezier;
44856     }()),
44857     
44858     throttleStroke: function(fn, wait) {
44859       if (wait === void 0) { wait = 250; }
44860       var previous = 0;
44861       var timeout = null;
44862       var result;
44863       var storedContext;
44864       var storedArgs;
44865       var later = function () {
44866           previous = Date.now();
44867           timeout = null;
44868           result = fn.apply(storedContext, storedArgs);
44869           if (!timeout) {
44870               storedContext = null;
44871               storedArgs = [];
44872           }
44873       };
44874       return function wrapper() {
44875           var args = [];
44876           for (var _i = 0; _i < arguments.length; _i++) {
44877               args[_i] = arguments[_i];
44878           }
44879           var now = Date.now();
44880           var remaining = wait - (now - previous);
44881           storedContext = this;
44882           storedArgs = args;
44883           if (remaining <= 0 || remaining > wait) {
44884               if (timeout) {
44885                   clearTimeout(timeout);
44886                   timeout = null;
44887               }
44888               previous = now;
44889               result = fn.apply(storedContext, storedArgs);
44890               if (!timeout) {
44891                   storedContext = null;
44892                   storedArgs = [];
44893               }
44894           }
44895           else if (!timeout) {
44896               timeout = window.setTimeout(later, remaining);
44897           }
44898           return result;
44899       };
44900   }
44901   
44902 });
44903
44904  
44905
44906