changes to menu
[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         
3724         this.triggerEl.on('click', this.onTriggerClick, this);
3725         
3726         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3727         
3728         if (!this.hideTrigger) {
3729             if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3730                 // dropdown toggle on the 'a' in BS4?
3731                 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3732             } else {
3733                 this.triggerEl.addClass('dropdown-toggle');
3734             }
3735         }
3736         if (Roo.isTouch) {
3737             this.el.on('touchstart'  , this.onTouch, this);
3738         }
3739         this.el.on('click' , this.onClick, this);
3740
3741         this.el.on("mouseover", this.onMouseOver, this);
3742         this.el.on("mouseout", this.onMouseOut, this);
3743         
3744     },
3745     
3746     findTargetItem : function(e)
3747     {
3748         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3749         if(!t){
3750             return false;
3751         }
3752         //Roo.log(t);         Roo.log(t.id);
3753         if(t && t.id){
3754             //Roo.log(this.menuitems);
3755             return this.menuitems.get(t.id);
3756             
3757             //return this.items.get(t.menuItemId);
3758         }
3759         
3760         return false;
3761     },
3762     
3763     onTouch : function(e) 
3764     {
3765         Roo.log("menu.onTouch");
3766         //e.stopEvent(); this make the user popdown broken
3767         this.onClick(e);
3768     },
3769     
3770     onClick : function(e)
3771     {
3772         Roo.log("menu.onClick");
3773         
3774         var t = this.findTargetItem(e);
3775         if(!t || t.isContainer){
3776             return;
3777         }
3778         Roo.log(e);
3779         /*
3780         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3781             if(t == this.activeItem && t.shouldDeactivate(e)){
3782                 this.activeItem.deactivate();
3783                 delete this.activeItem;
3784                 return;
3785             }
3786             if(t.canActivate){
3787                 this.setActiveItem(t, true);
3788             }
3789             return;
3790             
3791             
3792         }
3793         */
3794        
3795         Roo.log('pass click event');
3796         
3797         t.onClick(e);
3798         
3799         this.fireEvent("click", this, t, e);
3800         
3801         var _this = this;
3802         
3803         if(!t.href.length || t.href == '#'){
3804             (function() { _this.hide(); }).defer(100);
3805         }
3806         
3807     },
3808     
3809     onMouseOver : function(e){
3810         var t  = this.findTargetItem(e);
3811         //Roo.log(t);
3812         //if(t){
3813         //    if(t.canActivate && !t.disabled){
3814         //        this.setActiveItem(t, true);
3815         //    }
3816         //}
3817         
3818         this.fireEvent("mouseover", this, e, t);
3819     },
3820     isVisible : function(){
3821         return !this.hidden;
3822     },
3823     onMouseOut : function(e){
3824         var t  = this.findTargetItem(e);
3825         
3826         //if(t ){
3827         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3828         //        this.activeItem.deactivate();
3829         //        delete this.activeItem;
3830         //    }
3831         //}
3832         this.fireEvent("mouseout", this, e, t);
3833     },
3834     
3835     
3836     /**
3837      * Displays this menu relative to another element
3838      * @param {String/HTMLElement/Roo.Element} element The element to align to
3839      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3840      * the element (defaults to this.defaultAlign)
3841      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3842      */
3843     show : function(el, pos, parentMenu)
3844     {
3845         if (false === this.fireEvent("beforeshow", this)) {
3846             Roo.log("show canceled");
3847             return;
3848         }
3849         this.parentMenu = parentMenu;
3850         if(!this.el){
3851             this.render();
3852         }
3853         this.el.addClass('show'); // show otherwise we do not know how big we are..
3854          
3855         var xy = this.el.getAlignToXY(el, pos);
3856         
3857         // bl-tl << left align  below
3858         // tl-bl << left align 
3859         
3860         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3861             // if it goes to far to the right.. -> align left.
3862             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3863         }
3864         if(xy[0] < 0){
3865             // was left align - go right?
3866             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3867         }
3868         
3869         // goes down the bottom
3870         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3871            xy[1]  < 0 ){
3872             var a = this.align.replace('?', '').split('-');
3873             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3874             
3875         }
3876         
3877         this.showAt(  xy , parentMenu, false);
3878     },
3879      /**
3880      * Displays this menu at a specific xy position
3881      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3882      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3883      */
3884     showAt : function(xy, parentMenu, /* private: */_e){
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         if(_e !== false){
3890             this.fireEvent("beforeshow", this);
3891             //xy = this.el.adjustForConstraints(xy);
3892         }
3893         
3894         //this.el.show();
3895         this.hideMenuItems();
3896         this.hidden = false;
3897         this.triggerEl.addClass('open');
3898         this.el.addClass('show');
3899         
3900         
3901         
3902         // reassign x when hitting right
3903         
3904         // reassign y when hitting bottom
3905         
3906         // but the list may align on trigger left or trigger top... should it be a properity?
3907         
3908         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3909             this.el.setXY(xy);
3910         }
3911         
3912         this.focus();
3913         this.fireEvent("show", this);
3914     },
3915     
3916     focus : function(){
3917         return;
3918         if(!this.hidden){
3919             this.doFocus.defer(50, this);
3920         }
3921     },
3922
3923     doFocus : function(){
3924         if(!this.hidden){
3925             this.focusEl.focus();
3926         }
3927     },
3928
3929     /**
3930      * Hides this menu and optionally all parent menus
3931      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3932      */
3933     hide : function(deep)
3934     {
3935         if (false === this.fireEvent("beforehide", this)) {
3936             Roo.log("hide canceled");
3937             return;
3938         }
3939         this.hideMenuItems();
3940         if(this.el && this.isVisible()){
3941            
3942             if(this.activeItem){
3943                 this.activeItem.deactivate();
3944                 this.activeItem = null;
3945             }
3946             this.triggerEl.removeClass('open');;
3947             this.el.removeClass('show');
3948             this.hidden = true;
3949             this.fireEvent("hide", this);
3950         }
3951         if(deep === true && this.parentMenu){
3952             this.parentMenu.hide(true);
3953         }
3954     },
3955     
3956     onTriggerClick : function(e)
3957     {
3958         Roo.log('trigger click');
3959         
3960         var target = e.getTarget();
3961         
3962         Roo.log(target.nodeName.toLowerCase());
3963         
3964         if(target.nodeName.toLowerCase() === 'i'){
3965             e.preventDefault();
3966         }
3967         
3968     },
3969     
3970     onTriggerPress  : function(e)
3971     {
3972         Roo.log('trigger press');
3973         //Roo.log(e.getTarget());
3974        // Roo.log(this.triggerEl.dom);
3975        
3976         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3977         var pel = Roo.get(e.getTarget());
3978         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3979             Roo.log('is treeview or dropdown?');
3980             return;
3981         }
3982         
3983         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3984             return;
3985         }
3986         
3987         if (this.isVisible()) {
3988             Roo.log('hide');
3989             this.hide();
3990         } else {
3991             Roo.log('show');
3992              
3993             this.show(this.triggerEl, this.align, false);
3994         }
3995         
3996         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3997             e.stopEvent();
3998         }
3999         
4000     },
4001        
4002     
4003     hideMenuItems : function()
4004     {
4005         Roo.log("hide Menu Items");
4006         if (!this.el) { 
4007             return;
4008         }
4009         
4010         this.el.select('.open',true).each(function(aa) {
4011             
4012             aa.removeClass('open');
4013          
4014         });
4015     },
4016     addxtypeChild : function (tree, cntr) {
4017         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4018           
4019         this.menuitems.add(comp);
4020         return comp;
4021
4022     },
4023     getEl : function()
4024     {
4025         Roo.log(this.el);
4026         return this.el;
4027     },
4028     
4029     clear : function()
4030     {
4031         this.getEl().dom.innerHTML = '';
4032         this.menuitems.clear();
4033     }
4034 });
4035
4036  
4037  /*
4038  * - LGPL
4039  *
4040  * menu item
4041  * 
4042  */
4043
4044
4045 /**
4046  * @class Roo.bootstrap.MenuItem
4047  * @extends Roo.bootstrap.Component
4048  * Bootstrap MenuItem class
4049  * @cfg {String} html the menu label
4050  * @cfg {String} href the link
4051  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4052  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4053  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4054  * @cfg {String} fa favicon to show on left of menu item.
4055  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4056  * 
4057  * 
4058  * @constructor
4059  * Create a new MenuItem
4060  * @param {Object} config The config object
4061  */
4062
4063
4064 Roo.bootstrap.MenuItem = function(config){
4065     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4066     this.addEvents({
4067         // raw events
4068         /**
4069          * @event click
4070          * The raw click event for the entire grid.
4071          * @param {Roo.bootstrap.MenuItem} this
4072          * @param {Roo.EventObject} e
4073          */
4074         "click" : true
4075     });
4076 };
4077
4078 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4079     
4080     href : false,
4081     html : false,
4082     preventDefault: false,
4083     isContainer : false,
4084     active : false,
4085     fa: false,
4086     
4087     getAutoCreate : function(){
4088         
4089         if(this.isContainer){
4090             return {
4091                 tag: 'li',
4092                 cls: 'dropdown-menu-item '
4093             };
4094         }
4095         var ctag = {
4096             tag: 'span',
4097             html: 'Link'
4098         };
4099         
4100         var anc = {
4101             tag : 'a',
4102             cls : 'dropdown-item',
4103             href : '#',
4104             cn : [  ]
4105         };
4106         
4107         if (this.fa !== false) {
4108             anc.cn.push({
4109                 tag : 'i',
4110                 cls : 'fa fa-' + this.fa
4111             });
4112         }
4113         
4114         anc.cn.push(ctag);
4115         
4116         
4117         var cfg= {
4118             tag: 'li',
4119             cls: 'dropdown-menu-item',
4120             cn: [ anc ]
4121         };
4122         if (this.parent().type == 'treeview') {
4123             cfg.cls = 'treeview-menu';
4124         }
4125         if (this.active) {
4126             cfg.cls += ' active';
4127         }
4128         
4129         
4130         
4131         anc.href = this.href || cfg.cn[0].href ;
4132         ctag.html = this.html || cfg.cn[0].html ;
4133         return cfg;
4134     },
4135     
4136     initEvents: function()
4137     {
4138         if (this.parent().type == 'treeview') {
4139             this.el.select('a').on('click', this.onClick, this);
4140         }
4141         
4142         if (this.menu) {
4143             this.menu.parentType = this.xtype;
4144             this.menu.triggerEl = this.el;
4145             this.menu = this.addxtype(Roo.apply({}, this.menu));
4146         }
4147         
4148     },
4149     onClick : function(e)
4150     {
4151         Roo.log('item on click ');
4152         
4153         if(this.preventDefault){
4154             e.preventDefault();
4155         }
4156         //this.parent().hideMenuItems();
4157         
4158         this.fireEvent('click', this, e);
4159     },
4160     getEl : function()
4161     {
4162         return this.el;
4163     } 
4164 });
4165
4166  
4167
4168  /*
4169  * - LGPL
4170  *
4171  * menu separator
4172  * 
4173  */
4174
4175
4176 /**
4177  * @class Roo.bootstrap.MenuSeparator
4178  * @extends Roo.bootstrap.Component
4179  * Bootstrap MenuSeparator class
4180  * 
4181  * @constructor
4182  * Create a new MenuItem
4183  * @param {Object} config The config object
4184  */
4185
4186
4187 Roo.bootstrap.MenuSeparator = function(config){
4188     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4189 };
4190
4191 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4192     
4193     getAutoCreate : function(){
4194         var cfg = {
4195             cls: 'divider',
4196             tag : 'li'
4197         };
4198         
4199         return cfg;
4200     }
4201    
4202 });
4203
4204  
4205
4206  
4207 /*
4208 * Licence: LGPL
4209 */
4210
4211 /**
4212  * @class Roo.bootstrap.Modal
4213  * @extends Roo.bootstrap.Component
4214  * Bootstrap Modal class
4215  * @cfg {String} title Title of dialog
4216  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4217  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4218  * @cfg {Boolean} specificTitle default false
4219  * @cfg {Array} buttons Array of buttons or standard button set..
4220  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4221  * @cfg {Boolean} animate default true
4222  * @cfg {Boolean} allow_close default true
4223  * @cfg {Boolean} fitwindow default false
4224  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4225  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4226  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4227  * @cfg {String} size (sm|lg|xl) default empty
4228  * @cfg {Number} max_width set the max width of modal
4229  * @cfg {Boolean} editableTitle can the title be edited
4230
4231  *
4232  *
4233  * @constructor
4234  * Create a new Modal Dialog
4235  * @param {Object} config The config object
4236  */
4237
4238 Roo.bootstrap.Modal = function(config){
4239     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4240     this.addEvents({
4241         // raw events
4242         /**
4243          * @event btnclick
4244          * The raw btnclick event for the button
4245          * @param {Roo.EventObject} e
4246          */
4247         "btnclick" : true,
4248         /**
4249          * @event resize
4250          * Fire when dialog resize
4251          * @param {Roo.bootstrap.Modal} this
4252          * @param {Roo.EventObject} e
4253          */
4254         "resize" : true,
4255         /**
4256          * @event titlechanged
4257          * Fire when the editable title has been changed
4258          * @param {Roo.bootstrap.Modal} this
4259          * @param {Roo.EventObject} value
4260          */
4261         "titlechanged" : true 
4262         
4263     });
4264     this.buttons = this.buttons || [];
4265
4266     if (this.tmpl) {
4267         this.tmpl = Roo.factory(this.tmpl);
4268     }
4269
4270 };
4271
4272 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4273
4274     title : 'test dialog',
4275
4276     buttons : false,
4277
4278     // set on load...
4279
4280     html: false,
4281
4282     tmp: false,
4283
4284     specificTitle: false,
4285
4286     buttonPosition: 'right',
4287
4288     allow_close : true,
4289
4290     animate : true,
4291
4292     fitwindow: false,
4293     
4294      // private
4295     dialogEl: false,
4296     bodyEl:  false,
4297     footerEl:  false,
4298     titleEl:  false,
4299     closeEl:  false,
4300
4301     size: '',
4302     
4303     max_width: 0,
4304     
4305     max_height: 0,
4306     
4307     fit_content: false,
4308     editableTitle  : false,
4309
4310     onRender : function(ct, position)
4311     {
4312         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4313
4314         if(!this.el){
4315             var cfg = Roo.apply({},  this.getAutoCreate());
4316             cfg.id = Roo.id();
4317             //if(!cfg.name){
4318             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4319             //}
4320             //if (!cfg.name.length) {
4321             //    delete cfg.name;
4322            // }
4323             if (this.cls) {
4324                 cfg.cls += ' ' + this.cls;
4325             }
4326             if (this.style) {
4327                 cfg.style = this.style;
4328             }
4329             this.el = Roo.get(document.body).createChild(cfg, position);
4330         }
4331         //var type = this.el.dom.type;
4332
4333
4334         if(this.tabIndex !== undefined){
4335             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4336         }
4337
4338         this.dialogEl = this.el.select('.modal-dialog',true).first();
4339         this.bodyEl = this.el.select('.modal-body',true).first();
4340         this.closeEl = this.el.select('.modal-header .close', true).first();
4341         this.headerEl = this.el.select('.modal-header',true).first();
4342         this.titleEl = this.el.select('.modal-title',true).first();
4343         this.footerEl = this.el.select('.modal-footer',true).first();
4344
4345         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4346         
4347         //this.el.addClass("x-dlg-modal");
4348
4349         if (this.buttons.length) {
4350             Roo.each(this.buttons, function(bb) {
4351                 var b = Roo.apply({}, bb);
4352                 b.xns = b.xns || Roo.bootstrap;
4353                 b.xtype = b.xtype || 'Button';
4354                 if (typeof(b.listeners) == 'undefined') {
4355                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4356                 }
4357
4358                 var btn = Roo.factory(b);
4359
4360                 btn.render(this.getButtonContainer());
4361
4362             },this);
4363         }
4364         // render the children.
4365         var nitems = [];
4366
4367         if(typeof(this.items) != 'undefined'){
4368             var items = this.items;
4369             delete this.items;
4370
4371             for(var i =0;i < items.length;i++) {
4372                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4373             }
4374         }
4375
4376         this.items = nitems;
4377
4378         // where are these used - they used to be body/close/footer
4379
4380
4381         this.initEvents();
4382         //this.el.addClass([this.fieldClass, this.cls]);
4383
4384     },
4385
4386     getAutoCreate : function()
4387     {
4388         // we will default to modal-body-overflow - might need to remove or make optional later.
4389         var bdy = {
4390                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4391                 html : this.html || ''
4392         };
4393
4394         var title = {
4395             tag: 'h5',
4396             cls : 'modal-title',
4397             html : this.title
4398         };
4399
4400         if(this.specificTitle){ // WTF is this?
4401             title = this.title;
4402         }
4403
4404         var header = [];
4405         if (this.allow_close && Roo.bootstrap.version == 3) {
4406             header.push({
4407                 tag: 'button',
4408                 cls : 'close',
4409                 html : '&times'
4410             });
4411         }
4412
4413         header.push(title);
4414
4415         if (this.editableTitle) {
4416             header.push({
4417                 cls: 'form-control roo-editable-title d-none',
4418                 tag: 'input',
4419                 type: 'text'
4420             });
4421         }
4422         
4423         if (this.allow_close && Roo.bootstrap.version == 4) {
4424             header.push({
4425                 tag: 'button',
4426                 cls : 'close',
4427                 html : '&times'
4428             });
4429         }
4430         
4431         var size = '';
4432
4433         if(this.size.length){
4434             size = 'modal-' + this.size;
4435         }
4436         
4437         var footer = Roo.bootstrap.version == 3 ?
4438             {
4439                 cls : 'modal-footer',
4440                 cn : [
4441                     {
4442                         tag: 'div',
4443                         cls: 'btn-' + this.buttonPosition
4444                     }
4445                 ]
4446
4447             } :
4448             {  // BS4 uses mr-auto on left buttons....
4449                 cls : 'modal-footer'
4450             };
4451
4452             
4453
4454         
4455         
4456         var modal = {
4457             cls: "modal",
4458              cn : [
4459                 {
4460                     cls: "modal-dialog " + size,
4461                     cn : [
4462                         {
4463                             cls : "modal-content",
4464                             cn : [
4465                                 {
4466                                     cls : 'modal-header',
4467                                     cn : header
4468                                 },
4469                                 bdy,
4470                                 footer
4471                             ]
4472
4473                         }
4474                     ]
4475
4476                 }
4477             ]
4478         };
4479
4480         if(this.animate){
4481             modal.cls += ' fade';
4482         }
4483
4484         return modal;
4485
4486     },
4487     getChildContainer : function() {
4488
4489          return this.bodyEl;
4490
4491     },
4492     getButtonContainer : function() {
4493         
4494          return Roo.bootstrap.version == 4 ?
4495             this.el.select('.modal-footer',true).first()
4496             : this.el.select('.modal-footer div',true).first();
4497
4498     },
4499     initEvents : function()
4500     {
4501         if (this.allow_close) {
4502             this.closeEl.on('click', this.hide, this);
4503         }
4504         Roo.EventManager.onWindowResize(this.resize, this, true);
4505         if (this.editableTitle) {
4506             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4507             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4508             this.headerEditEl.on('keyup', function(e) {
4509                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4510                         this.toggleHeaderInput(false)
4511                     }
4512                 }, this);
4513             this.headerEditEl.on('blur', function(e) {
4514                 this.toggleHeaderInput(false)
4515             },this);
4516         }
4517
4518     },
4519   
4520
4521     resize : function()
4522     {
4523         this.maskEl.setSize(
4524             Roo.lib.Dom.getViewWidth(true),
4525             Roo.lib.Dom.getViewHeight(true)
4526         );
4527         
4528         if (this.fitwindow) {
4529             
4530            this.dialogEl.setStyle( { 'max-width' : '100%' });
4531             this.setSize(
4532                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4533                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4534             );
4535             return;
4536         }
4537         
4538         if(this.max_width !== 0) {
4539             
4540             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4541             
4542             if(this.height) {
4543                 this.setSize(w, this.height);
4544                 return;
4545             }
4546             
4547             if(this.max_height) {
4548                 this.setSize(w,Math.min(
4549                     this.max_height,
4550                     Roo.lib.Dom.getViewportHeight(true) - 60
4551                 ));
4552                 
4553                 return;
4554             }
4555             
4556             if(!this.fit_content) {
4557                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4558                 return;
4559             }
4560             
4561             this.setSize(w, Math.min(
4562                 60 +
4563                 this.headerEl.getHeight() + 
4564                 this.footerEl.getHeight() + 
4565                 this.getChildHeight(this.bodyEl.dom.childNodes),
4566                 Roo.lib.Dom.getViewportHeight(true) - 60)
4567             );
4568         }
4569         
4570     },
4571
4572     setSize : function(w,h)
4573     {
4574         if (!w && !h) {
4575             return;
4576         }
4577         
4578         this.resizeTo(w,h);
4579     },
4580
4581     show : function() {
4582
4583         if (!this.rendered) {
4584             this.render();
4585         }
4586         this.toggleHeaderInput(false);
4587         //this.el.setStyle('display', 'block');
4588         this.el.removeClass('hideing');
4589         this.el.dom.style.display='block';
4590         
4591         Roo.get(document.body).addClass('modal-open');
4592  
4593         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4594             
4595             (function(){
4596                 this.el.addClass('show');
4597                 this.el.addClass('in');
4598             }).defer(50, this);
4599         }else{
4600             this.el.addClass('show');
4601             this.el.addClass('in');
4602         }
4603
4604         // not sure how we can show data in here..
4605         //if (this.tmpl) {
4606         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4607         //}
4608
4609         Roo.get(document.body).addClass("x-body-masked");
4610         
4611         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4612         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4613         this.maskEl.dom.style.display = 'block';
4614         this.maskEl.addClass('show');
4615         
4616         
4617         this.resize();
4618         
4619         this.fireEvent('show', this);
4620
4621         // set zindex here - otherwise it appears to be ignored...
4622         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4623
4624         (function () {
4625             this.items.forEach( function(e) {
4626                 e.layout ? e.layout() : false;
4627
4628             });
4629         }).defer(100,this);
4630
4631     },
4632     hide : function()
4633     {
4634         if(this.fireEvent("beforehide", this) !== false){
4635             
4636             this.maskEl.removeClass('show');
4637             
4638             this.maskEl.dom.style.display = '';
4639             Roo.get(document.body).removeClass("x-body-masked");
4640             this.el.removeClass('in');
4641             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4642
4643             if(this.animate){ // why
4644                 this.el.addClass('hideing');
4645                 this.el.removeClass('show');
4646                 (function(){
4647                     if (!this.el.hasClass('hideing')) {
4648                         return; // it's been shown again...
4649                     }
4650                     
4651                     this.el.dom.style.display='';
4652
4653                     Roo.get(document.body).removeClass('modal-open');
4654                     this.el.removeClass('hideing');
4655                 }).defer(150,this);
4656                 
4657             }else{
4658                 this.el.removeClass('show');
4659                 this.el.dom.style.display='';
4660                 Roo.get(document.body).removeClass('modal-open');
4661
4662             }
4663             this.fireEvent('hide', this);
4664         }
4665     },
4666     isVisible : function()
4667     {
4668         
4669         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4670         
4671     },
4672
4673     addButton : function(str, cb)
4674     {
4675
4676
4677         var b = Roo.apply({}, { html : str } );
4678         b.xns = b.xns || Roo.bootstrap;
4679         b.xtype = b.xtype || 'Button';
4680         if (typeof(b.listeners) == 'undefined') {
4681             b.listeners = { click : cb.createDelegate(this)  };
4682         }
4683
4684         var btn = Roo.factory(b);
4685
4686         btn.render(this.getButtonContainer());
4687
4688         return btn;
4689
4690     },
4691
4692     setDefaultButton : function(btn)
4693     {
4694         //this.el.select('.modal-footer').()
4695     },
4696
4697     resizeTo: function(w,h)
4698     {
4699         this.dialogEl.setWidth(w);
4700         
4701         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4702
4703         this.bodyEl.setHeight(h - diff);
4704         
4705         this.fireEvent('resize', this);
4706     },
4707     
4708     setContentSize  : function(w, h)
4709     {
4710
4711     },
4712     onButtonClick: function(btn,e)
4713     {
4714         //Roo.log([a,b,c]);
4715         this.fireEvent('btnclick', btn.name, e);
4716     },
4717      /**
4718      * Set the title of the Dialog
4719      * @param {String} str new Title
4720      */
4721     setTitle: function(str) {
4722         this.titleEl.dom.innerHTML = str;
4723         this.title = str;
4724     },
4725     /**
4726      * Set the body of the Dialog
4727      * @param {String} str new Title
4728      */
4729     setBody: function(str) {
4730         this.bodyEl.dom.innerHTML = str;
4731     },
4732     /**
4733      * Set the body of the Dialog using the template
4734      * @param {Obj} data - apply this data to the template and replace the body contents.
4735      */
4736     applyBody: function(obj)
4737     {
4738         if (!this.tmpl) {
4739             Roo.log("Error - using apply Body without a template");
4740             //code
4741         }
4742         this.tmpl.overwrite(this.bodyEl, obj);
4743     },
4744     
4745     getChildHeight : function(child_nodes)
4746     {
4747         if(
4748             !child_nodes ||
4749             child_nodes.length == 0
4750         ) {
4751             return 0;
4752         }
4753         
4754         var child_height = 0;
4755         
4756         for(var i = 0; i < child_nodes.length; i++) {
4757             
4758             /*
4759             * for modal with tabs...
4760             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4761                 
4762                 var layout_childs = child_nodes[i].childNodes;
4763                 
4764                 for(var j = 0; j < layout_childs.length; j++) {
4765                     
4766                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4767                         
4768                         var layout_body_childs = layout_childs[j].childNodes;
4769                         
4770                         for(var k = 0; k < layout_body_childs.length; k++) {
4771                             
4772                             if(layout_body_childs[k].classList.contains('navbar')) {
4773                                 child_height += layout_body_childs[k].offsetHeight;
4774                                 continue;
4775                             }
4776                             
4777                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4778                                 
4779                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4780                                 
4781                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4782                                     
4783                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4784                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4785                                         continue;
4786                                     }
4787                                     
4788                                 }
4789                                 
4790                             }
4791                             
4792                         }
4793                     }
4794                 }
4795                 continue;
4796             }
4797             */
4798             
4799             child_height += child_nodes[i].offsetHeight;
4800             // Roo.log(child_nodes[i].offsetHeight);
4801         }
4802         
4803         return child_height;
4804     },
4805     toggleHeaderInput : function(is_edit)
4806     {
4807         if (!this.editableTitle) {
4808             return; // not editable.
4809         }
4810         if (is_edit && this.is_header_editing) {
4811             return; // already editing..
4812         }
4813         if (is_edit) {
4814     
4815             this.headerEditEl.dom.value = this.title;
4816             this.headerEditEl.removeClass('d-none');
4817             this.headerEditEl.dom.focus();
4818             this.titleEl.addClass('d-none');
4819             
4820             this.is_header_editing = true;
4821             return
4822         }
4823         // flip back to not editing.
4824         this.title = this.headerEditEl.dom.value;
4825         this.headerEditEl.addClass('d-none');
4826         this.titleEl.removeClass('d-none');
4827         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4828         this.is_header_editing = false;
4829         this.fireEvent('titlechanged', this, this.title);
4830     
4831             
4832         
4833     }
4834
4835 });
4836
4837
4838 Roo.apply(Roo.bootstrap.Modal,  {
4839     /**
4840          * Button config that displays a single OK button
4841          * @type Object
4842          */
4843         OK :  [{
4844             name : 'ok',
4845             weight : 'primary',
4846             html : 'OK'
4847         }],
4848         /**
4849          * Button config that displays Yes and No buttons
4850          * @type Object
4851          */
4852         YESNO : [
4853             {
4854                 name  : 'no',
4855                 html : 'No'
4856             },
4857             {
4858                 name  :'yes',
4859                 weight : 'primary',
4860                 html : 'Yes'
4861             }
4862         ],
4863
4864         /**
4865          * Button config that displays OK and Cancel buttons
4866          * @type Object
4867          */
4868         OKCANCEL : [
4869             {
4870                name : 'cancel',
4871                 html : 'Cancel'
4872             },
4873             {
4874                 name : 'ok',
4875                 weight : 'primary',
4876                 html : 'OK'
4877             }
4878         ],
4879         /**
4880          * Button config that displays Yes, No and Cancel buttons
4881          * @type Object
4882          */
4883         YESNOCANCEL : [
4884             {
4885                 name : 'yes',
4886                 weight : 'primary',
4887                 html : 'Yes'
4888             },
4889             {
4890                 name : 'no',
4891                 html : 'No'
4892             },
4893             {
4894                 name : 'cancel',
4895                 html : 'Cancel'
4896             }
4897         ],
4898         
4899         zIndex : 10001
4900 });
4901
4902 /*
4903  * - LGPL
4904  *
4905  * messagebox - can be used as a replace
4906  * 
4907  */
4908 /**
4909  * @class Roo.MessageBox
4910  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4911  * Example usage:
4912  *<pre><code>
4913 // Basic alert:
4914 Roo.Msg.alert('Status', 'Changes saved successfully.');
4915
4916 // Prompt for user data:
4917 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4918     if (btn == 'ok'){
4919         // process text value...
4920     }
4921 });
4922
4923 // Show a dialog using config options:
4924 Roo.Msg.show({
4925    title:'Save Changes?',
4926    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4927    buttons: Roo.Msg.YESNOCANCEL,
4928    fn: processResult,
4929    animEl: 'elId'
4930 });
4931 </code></pre>
4932  * @singleton
4933  */
4934 Roo.bootstrap.MessageBox = function(){
4935     var dlg, opt, mask, waitTimer;
4936     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4937     var buttons, activeTextEl, bwidth;
4938
4939     
4940     // private
4941     var handleButton = function(button){
4942         dlg.hide();
4943         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4944     };
4945
4946     // private
4947     var handleHide = function(){
4948         if(opt && opt.cls){
4949             dlg.el.removeClass(opt.cls);
4950         }
4951         //if(waitTimer){
4952         //    Roo.TaskMgr.stop(waitTimer);
4953         //    waitTimer = null;
4954         //}
4955     };
4956
4957     // private
4958     var updateButtons = function(b){
4959         var width = 0;
4960         if(!b){
4961             buttons["ok"].hide();
4962             buttons["cancel"].hide();
4963             buttons["yes"].hide();
4964             buttons["no"].hide();
4965             dlg.footerEl.hide();
4966             
4967             return width;
4968         }
4969         dlg.footerEl.show();
4970         for(var k in buttons){
4971             if(typeof buttons[k] != "function"){
4972                 if(b[k]){
4973                     buttons[k].show();
4974                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4975                     width += buttons[k].el.getWidth()+15;
4976                 }else{
4977                     buttons[k].hide();
4978                 }
4979             }
4980         }
4981         return width;
4982     };
4983
4984     // private
4985     var handleEsc = function(d, k, e){
4986         if(opt && opt.closable !== false){
4987             dlg.hide();
4988         }
4989         if(e){
4990             e.stopEvent();
4991         }
4992     };
4993
4994     return {
4995         /**
4996          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4997          * @return {Roo.BasicDialog} The BasicDialog element
4998          */
4999         getDialog : function(){
5000            if(!dlg){
5001                 dlg = new Roo.bootstrap.Modal( {
5002                     //draggable: true,
5003                     //resizable:false,
5004                     //constraintoviewport:false,
5005                     //fixedcenter:true,
5006                     //collapsible : false,
5007                     //shim:true,
5008                     //modal: true,
5009                 //    width: 'auto',
5010                   //  height:100,
5011                     //buttonAlign:"center",
5012                     closeClick : function(){
5013                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5014                             handleButton("no");
5015                         }else{
5016                             handleButton("cancel");
5017                         }
5018                     }
5019                 });
5020                 dlg.render();
5021                 dlg.on("hide", handleHide);
5022                 mask = dlg.mask;
5023                 //dlg.addKeyListener(27, handleEsc);
5024                 buttons = {};
5025                 this.buttons = buttons;
5026                 var bt = this.buttonText;
5027                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5028                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5029                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5030                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5031                 //Roo.log(buttons);
5032                 bodyEl = dlg.bodyEl.createChild({
5033
5034                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5035                         '<textarea class="roo-mb-textarea"></textarea>' +
5036                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5037                 });
5038                 msgEl = bodyEl.dom.firstChild;
5039                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5040                 textboxEl.enableDisplayMode();
5041                 textboxEl.addKeyListener([10,13], function(){
5042                     if(dlg.isVisible() && opt && opt.buttons){
5043                         if(opt.buttons.ok){
5044                             handleButton("ok");
5045                         }else if(opt.buttons.yes){
5046                             handleButton("yes");
5047                         }
5048                     }
5049                 });
5050                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5051                 textareaEl.enableDisplayMode();
5052                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5053                 progressEl.enableDisplayMode();
5054                 
5055                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5056                 var pf = progressEl.dom.firstChild;
5057                 if (pf) {
5058                     pp = Roo.get(pf.firstChild);
5059                     pp.setHeight(pf.offsetHeight);
5060                 }
5061                 
5062             }
5063             return dlg;
5064         },
5065
5066         /**
5067          * Updates the message box body text
5068          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5069          * the XHTML-compliant non-breaking space character '&amp;#160;')
5070          * @return {Roo.MessageBox} This message box
5071          */
5072         updateText : function(text)
5073         {
5074             if(!dlg.isVisible() && !opt.width){
5075                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5076                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5077             }
5078             msgEl.innerHTML = text || '&#160;';
5079       
5080             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5081             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5082             var w = Math.max(
5083                     Math.min(opt.width || cw , this.maxWidth), 
5084                     Math.max(opt.minWidth || this.minWidth, bwidth)
5085             );
5086             if(opt.prompt){
5087                 activeTextEl.setWidth(w);
5088             }
5089             if(dlg.isVisible()){
5090                 dlg.fixedcenter = false;
5091             }
5092             // to big, make it scroll. = But as usual stupid IE does not support
5093             // !important..
5094             
5095             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5096                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5097                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5098             } else {
5099                 bodyEl.dom.style.height = '';
5100                 bodyEl.dom.style.overflowY = '';
5101             }
5102             if (cw > w) {
5103                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5104             } else {
5105                 bodyEl.dom.style.overflowX = '';
5106             }
5107             
5108             dlg.setContentSize(w, bodyEl.getHeight());
5109             if(dlg.isVisible()){
5110                 dlg.fixedcenter = true;
5111             }
5112             return this;
5113         },
5114
5115         /**
5116          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5117          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5118          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5119          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5120          * @return {Roo.MessageBox} This message box
5121          */
5122         updateProgress : function(value, text){
5123             if(text){
5124                 this.updateText(text);
5125             }
5126             
5127             if (pp) { // weird bug on my firefox - for some reason this is not defined
5128                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5129                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5130             }
5131             return this;
5132         },        
5133
5134         /**
5135          * Returns true if the message box is currently displayed
5136          * @return {Boolean} True if the message box is visible, else false
5137          */
5138         isVisible : function(){
5139             return dlg && dlg.isVisible();  
5140         },
5141
5142         /**
5143          * Hides the message box if it is displayed
5144          */
5145         hide : function(){
5146             if(this.isVisible()){
5147                 dlg.hide();
5148             }  
5149         },
5150
5151         /**
5152          * Displays a new message box, or reinitializes an existing message box, based on the config options
5153          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5154          * The following config object properties are supported:
5155          * <pre>
5156 Property    Type             Description
5157 ----------  ---------------  ------------------------------------------------------------------------------------
5158 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5159                                    closes (defaults to undefined)
5160 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5161                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5162 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5163                                    progress and wait dialogs will ignore this property and always hide the
5164                                    close button as they can only be closed programmatically.
5165 cls               String           A custom CSS class to apply to the message box element
5166 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5167                                    displayed (defaults to 75)
5168 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5169                                    function will be btn (the name of the button that was clicked, if applicable,
5170                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5171                                    Progress and wait dialogs will ignore this option since they do not respond to
5172                                    user actions and can only be closed programmatically, so any required function
5173                                    should be called by the same code after it closes the dialog.
5174 icon              String           A CSS class that provides a background image to be used as an icon for
5175                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5176 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5177 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5178 modal             Boolean          False to allow user interaction with the page while the message box is
5179                                    displayed (defaults to true)
5180 msg               String           A string that will replace the existing message box body text (defaults
5181                                    to the XHTML-compliant non-breaking space character '&#160;')
5182 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5183 progress          Boolean          True to display a progress bar (defaults to false)
5184 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5185 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5186 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5187 title             String           The title text
5188 value             String           The string value to set into the active textbox element if displayed
5189 wait              Boolean          True to display a progress bar (defaults to false)
5190 width             Number           The width of the dialog in pixels
5191 </pre>
5192          *
5193          * Example usage:
5194          * <pre><code>
5195 Roo.Msg.show({
5196    title: 'Address',
5197    msg: 'Please enter your address:',
5198    width: 300,
5199    buttons: Roo.MessageBox.OKCANCEL,
5200    multiline: true,
5201    fn: saveAddress,
5202    animEl: 'addAddressBtn'
5203 });
5204 </code></pre>
5205          * @param {Object} config Configuration options
5206          * @return {Roo.MessageBox} This message box
5207          */
5208         show : function(options)
5209         {
5210             
5211             // this causes nightmares if you show one dialog after another
5212             // especially on callbacks..
5213              
5214             if(this.isVisible()){
5215                 
5216                 this.hide();
5217                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5218                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5219                 Roo.log("New Dialog Message:" +  options.msg )
5220                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5221                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5222                 
5223             }
5224             var d = this.getDialog();
5225             opt = options;
5226             d.setTitle(opt.title || "&#160;");
5227             d.closeEl.setDisplayed(opt.closable !== false);
5228             activeTextEl = textboxEl;
5229             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5230             if(opt.prompt){
5231                 if(opt.multiline){
5232                     textboxEl.hide();
5233                     textareaEl.show();
5234                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5235                         opt.multiline : this.defaultTextHeight);
5236                     activeTextEl = textareaEl;
5237                 }else{
5238                     textboxEl.show();
5239                     textareaEl.hide();
5240                 }
5241             }else{
5242                 textboxEl.hide();
5243                 textareaEl.hide();
5244             }
5245             progressEl.setDisplayed(opt.progress === true);
5246             if (opt.progress) {
5247                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5248             }
5249             this.updateProgress(0);
5250             activeTextEl.dom.value = opt.value || "";
5251             if(opt.prompt){
5252                 dlg.setDefaultButton(activeTextEl);
5253             }else{
5254                 var bs = opt.buttons;
5255                 var db = null;
5256                 if(bs && bs.ok){
5257                     db = buttons["ok"];
5258                 }else if(bs && bs.yes){
5259                     db = buttons["yes"];
5260                 }
5261                 dlg.setDefaultButton(db);
5262             }
5263             bwidth = updateButtons(opt.buttons);
5264             this.updateText(opt.msg);
5265             if(opt.cls){
5266                 d.el.addClass(opt.cls);
5267             }
5268             d.proxyDrag = opt.proxyDrag === true;
5269             d.modal = opt.modal !== false;
5270             d.mask = opt.modal !== false ? mask : false;
5271             if(!d.isVisible()){
5272                 // force it to the end of the z-index stack so it gets a cursor in FF
5273                 document.body.appendChild(dlg.el.dom);
5274                 d.animateTarget = null;
5275                 d.show(options.animEl);
5276             }
5277             return this;
5278         },
5279
5280         /**
5281          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5282          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5283          * and closing the message box when the process is complete.
5284          * @param {String} title The title bar text
5285          * @param {String} msg The message box body text
5286          * @return {Roo.MessageBox} This message box
5287          */
5288         progress : function(title, msg){
5289             this.show({
5290                 title : title,
5291                 msg : msg,
5292                 buttons: false,
5293                 progress:true,
5294                 closable:false,
5295                 minWidth: this.minProgressWidth,
5296                 modal : true
5297             });
5298             return this;
5299         },
5300
5301         /**
5302          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5303          * If a callback function is passed it will be called after the user clicks the button, and the
5304          * id of the button that was clicked will be passed as the only parameter to the callback
5305          * (could also be the top-right close button).
5306          * @param {String} title The title bar text
5307          * @param {String} msg The message box body text
5308          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5309          * @param {Object} scope (optional) The scope of the callback function
5310          * @return {Roo.MessageBox} This message box
5311          */
5312         alert : function(title, msg, fn, scope)
5313         {
5314             this.show({
5315                 title : title,
5316                 msg : msg,
5317                 buttons: this.OK,
5318                 fn: fn,
5319                 closable : false,
5320                 scope : scope,
5321                 modal : true
5322             });
5323             return this;
5324         },
5325
5326         /**
5327          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5328          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5329          * You are responsible for closing the message box when the process is complete.
5330          * @param {String} msg The message box body text
5331          * @param {String} title (optional) The title bar text
5332          * @return {Roo.MessageBox} This message box
5333          */
5334         wait : function(msg, title){
5335             this.show({
5336                 title : title,
5337                 msg : msg,
5338                 buttons: false,
5339                 closable:false,
5340                 progress:true,
5341                 modal:true,
5342                 width:300,
5343                 wait:true
5344             });
5345             waitTimer = Roo.TaskMgr.start({
5346                 run: function(i){
5347                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5348                 },
5349                 interval: 1000
5350             });
5351             return this;
5352         },
5353
5354         /**
5355          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5356          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5357          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5358          * @param {String} title The title bar text
5359          * @param {String} msg The message box body text
5360          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5361          * @param {Object} scope (optional) The scope of the callback function
5362          * @return {Roo.MessageBox} This message box
5363          */
5364         confirm : function(title, msg, fn, scope){
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: this.YESNO,
5369                 fn: fn,
5370                 scope : scope,
5371                 modal : true
5372             });
5373             return this;
5374         },
5375
5376         /**
5377          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5378          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5379          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5380          * (could also be the top-right close button) and the text that was entered will be passed as the two
5381          * parameters to the callback.
5382          * @param {String} title The title bar text
5383          * @param {String} msg The message box body text
5384          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5385          * @param {Object} scope (optional) The scope of the callback function
5386          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5387          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5388          * @return {Roo.MessageBox} This message box
5389          */
5390         prompt : function(title, msg, fn, scope, multiline){
5391             this.show({
5392                 title : title,
5393                 msg : msg,
5394                 buttons: this.OKCANCEL,
5395                 fn: fn,
5396                 minWidth:250,
5397                 scope : scope,
5398                 prompt:true,
5399                 multiline: multiline,
5400                 modal : true
5401             });
5402             return this;
5403         },
5404
5405         /**
5406          * Button config that displays a single OK button
5407          * @type Object
5408          */
5409         OK : {ok:true},
5410         /**
5411          * Button config that displays Yes and No buttons
5412          * @type Object
5413          */
5414         YESNO : {yes:true, no:true},
5415         /**
5416          * Button config that displays OK and Cancel buttons
5417          * @type Object
5418          */
5419         OKCANCEL : {ok:true, cancel:true},
5420         /**
5421          * Button config that displays Yes, No and Cancel buttons
5422          * @type Object
5423          */
5424         YESNOCANCEL : {yes:true, no:true, cancel:true},
5425
5426         /**
5427          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5428          * @type Number
5429          */
5430         defaultTextHeight : 75,
5431         /**
5432          * The maximum width in pixels of the message box (defaults to 600)
5433          * @type Number
5434          */
5435         maxWidth : 600,
5436         /**
5437          * The minimum width in pixels of the message box (defaults to 100)
5438          * @type Number
5439          */
5440         minWidth : 100,
5441         /**
5442          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5443          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5444          * @type Number
5445          */
5446         minProgressWidth : 250,
5447         /**
5448          * An object containing the default button text strings that can be overriden for localized language support.
5449          * Supported properties are: ok, cancel, yes and no.
5450          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5451          * @type Object
5452          */
5453         buttonText : {
5454             ok : "OK",
5455             cancel : "Cancel",
5456             yes : "Yes",
5457             no : "No"
5458         }
5459     };
5460 }();
5461
5462 /**
5463  * Shorthand for {@link Roo.MessageBox}
5464  */
5465 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5466 Roo.Msg = Roo.Msg || Roo.MessageBox;
5467 /*
5468  * - LGPL
5469  *
5470  * navbar
5471  * 
5472  */
5473
5474 /**
5475  * @class Roo.bootstrap.Navbar
5476  * @extends Roo.bootstrap.Component
5477  * Bootstrap Navbar class
5478
5479  * @constructor
5480  * Create a new Navbar
5481  * @param {Object} config The config object
5482  */
5483
5484
5485 Roo.bootstrap.Navbar = function(config){
5486     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5487     this.addEvents({
5488         // raw events
5489         /**
5490          * @event beforetoggle
5491          * Fire before toggle the menu
5492          * @param {Roo.EventObject} e
5493          */
5494         "beforetoggle" : true
5495     });
5496 };
5497
5498 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5499     
5500     
5501    
5502     // private
5503     navItems : false,
5504     loadMask : false,
5505     
5506     
5507     getAutoCreate : function(){
5508         
5509         
5510         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5511         
5512     },
5513     
5514     initEvents :function ()
5515     {
5516         //Roo.log(this.el.select('.navbar-toggle',true));
5517         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5518         
5519         var mark = {
5520             tag: "div",
5521             cls:"x-dlg-mask"
5522         };
5523         
5524         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5525         
5526         var size = this.el.getSize();
5527         this.maskEl.setSize(size.width, size.height);
5528         this.maskEl.enableDisplayMode("block");
5529         this.maskEl.hide();
5530         
5531         if(this.loadMask){
5532             this.maskEl.show();
5533         }
5534     },
5535     
5536     
5537     getChildContainer : function()
5538     {
5539         if (this.el && this.el.select('.collapse').getCount()) {
5540             return this.el.select('.collapse',true).first();
5541         }
5542         
5543         return this.el;
5544     },
5545     
5546     mask : function()
5547     {
5548         this.maskEl.show();
5549     },
5550     
5551     unmask : function()
5552     {
5553         this.maskEl.hide();
5554     },
5555     onToggle : function()
5556     {
5557         
5558         if(this.fireEvent('beforetoggle', this) === false){
5559             return;
5560         }
5561         var ce = this.el.select('.navbar-collapse',true).first();
5562       
5563         if (!ce.hasClass('show')) {
5564            this.expand();
5565         } else {
5566             this.collapse();
5567         }
5568         
5569         
5570     
5571     },
5572     /**
5573      * Expand the navbar pulldown 
5574      */
5575     expand : function ()
5576     {
5577        
5578         var ce = this.el.select('.navbar-collapse',true).first();
5579         if (ce.hasClass('collapsing')) {
5580             return;
5581         }
5582         ce.dom.style.height = '';
5583                // show it...
5584         ce.addClass('in'); // old...
5585         ce.removeClass('collapse');
5586         ce.addClass('show');
5587         var h = ce.getHeight();
5588         Roo.log(h);
5589         ce.removeClass('show');
5590         // at this point we should be able to see it..
5591         ce.addClass('collapsing');
5592         
5593         ce.setHeight(0); // resize it ...
5594         ce.on('transitionend', function() {
5595             //Roo.log('done transition');
5596             ce.removeClass('collapsing');
5597             ce.addClass('show');
5598             ce.removeClass('collapse');
5599
5600             ce.dom.style.height = '';
5601         }, this, { single: true} );
5602         ce.setHeight(h);
5603         ce.dom.scrollTop = 0;
5604     },
5605     /**
5606      * Collapse the navbar pulldown 
5607      */
5608     collapse : function()
5609     {
5610          var ce = this.el.select('.navbar-collapse',true).first();
5611        
5612         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5613             // it's collapsed or collapsing..
5614             return;
5615         }
5616         ce.removeClass('in'); // old...
5617         ce.setHeight(ce.getHeight());
5618         ce.removeClass('show');
5619         ce.addClass('collapsing');
5620         
5621         ce.on('transitionend', function() {
5622             ce.dom.style.height = '';
5623             ce.removeClass('collapsing');
5624             ce.addClass('collapse');
5625         }, this, { single: true} );
5626         ce.setHeight(0);
5627     }
5628     
5629     
5630     
5631 });
5632
5633
5634
5635  
5636
5637  /*
5638  * - LGPL
5639  *
5640  * navbar
5641  * 
5642  */
5643
5644 /**
5645  * @class Roo.bootstrap.NavSimplebar
5646  * @extends Roo.bootstrap.Navbar
5647  * Bootstrap Sidebar class
5648  *
5649  * @cfg {Boolean} inverse is inverted color
5650  * 
5651  * @cfg {String} type (nav | pills | tabs)
5652  * @cfg {Boolean} arrangement stacked | justified
5653  * @cfg {String} align (left | right) alignment
5654  * 
5655  * @cfg {Boolean} main (true|false) main nav bar? default false
5656  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5657  * 
5658  * @cfg {String} tag (header|footer|nav|div) default is nav 
5659
5660  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5661  * 
5662  * 
5663  * @constructor
5664  * Create a new Sidebar
5665  * @param {Object} config The config object
5666  */
5667
5668
5669 Roo.bootstrap.NavSimplebar = function(config){
5670     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5671 };
5672
5673 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5674     
5675     inverse: false,
5676     
5677     type: false,
5678     arrangement: '',
5679     align : false,
5680     
5681     weight : 'light',
5682     
5683     main : false,
5684     
5685     
5686     tag : false,
5687     
5688     
5689     getAutoCreate : function(){
5690         
5691         
5692         var cfg = {
5693             tag : this.tag || 'div',
5694             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5695         };
5696         if (['light','white'].indexOf(this.weight) > -1) {
5697             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5698         }
5699         cfg.cls += ' bg-' + this.weight;
5700         
5701         if (this.inverse) {
5702             cfg.cls += ' navbar-inverse';
5703             
5704         }
5705         
5706         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5707         
5708         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5709             return cfg;
5710         }
5711         
5712         
5713     
5714         
5715         cfg.cn = [
5716             {
5717                 cls: 'nav nav-' + this.xtype,
5718                 tag : 'ul'
5719             }
5720         ];
5721         
5722          
5723         this.type = this.type || 'nav';
5724         if (['tabs','pills'].indexOf(this.type) != -1) {
5725             cfg.cn[0].cls += ' nav-' + this.type
5726         
5727         
5728         } else {
5729             if (this.type!=='nav') {
5730                 Roo.log('nav type must be nav/tabs/pills')
5731             }
5732             cfg.cn[0].cls += ' navbar-nav'
5733         }
5734         
5735         
5736         
5737         
5738         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5739             cfg.cn[0].cls += ' nav-' + this.arrangement;
5740         }
5741         
5742         
5743         if (this.align === 'right') {
5744             cfg.cn[0].cls += ' navbar-right';
5745         }
5746         
5747         
5748         
5749         
5750         return cfg;
5751     
5752         
5753     }
5754     
5755     
5756     
5757 });
5758
5759
5760
5761  
5762
5763  
5764        /*
5765  * - LGPL
5766  *
5767  * navbar
5768  * navbar-fixed-top
5769  * navbar-expand-md  fixed-top 
5770  */
5771
5772 /**
5773  * @class Roo.bootstrap.NavHeaderbar
5774  * @extends Roo.bootstrap.NavSimplebar
5775  * Bootstrap Sidebar class
5776  *
5777  * @cfg {String} brand what is brand
5778  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5779  * @cfg {String} brand_href href of the brand
5780  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5781  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5782  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5783  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5784  * 
5785  * @constructor
5786  * Create a new Sidebar
5787  * @param {Object} config The config object
5788  */
5789
5790
5791 Roo.bootstrap.NavHeaderbar = function(config){
5792     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5793       
5794 };
5795
5796 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5797     
5798     position: '',
5799     brand: '',
5800     brand_href: false,
5801     srButton : true,
5802     autohide : false,
5803     desktopCenter : false,
5804    
5805     
5806     getAutoCreate : function(){
5807         
5808         var   cfg = {
5809             tag: this.nav || 'nav',
5810             cls: 'navbar navbar-expand-md',
5811             role: 'navigation',
5812             cn: []
5813         };
5814         
5815         var cn = cfg.cn;
5816         if (this.desktopCenter) {
5817             cn.push({cls : 'container', cn : []});
5818             cn = cn[0].cn;
5819         }
5820         
5821         if(this.srButton){
5822             var btn = {
5823                 tag: 'button',
5824                 type: 'button',
5825                 cls: 'navbar-toggle navbar-toggler',
5826                 'data-toggle': 'collapse',
5827                 cn: [
5828                     {
5829                         tag: 'span',
5830                         cls: 'sr-only',
5831                         html: 'Toggle navigation'
5832                     },
5833                     {
5834                         tag: 'span',
5835                         cls: 'icon-bar navbar-toggler-icon'
5836                     },
5837                     {
5838                         tag: 'span',
5839                         cls: 'icon-bar'
5840                     },
5841                     {
5842                         tag: 'span',
5843                         cls: 'icon-bar'
5844                     }
5845                 ]
5846             };
5847             
5848             cn.push( Roo.bootstrap.version == 4 ? btn : {
5849                 tag: 'div',
5850                 cls: 'navbar-header',
5851                 cn: [
5852                     btn
5853                 ]
5854             });
5855         }
5856         
5857         cn.push({
5858             tag: 'div',
5859             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5860             cn : []
5861         });
5862         
5863         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5864         
5865         if (['light','white'].indexOf(this.weight) > -1) {
5866             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5867         }
5868         cfg.cls += ' bg-' + this.weight;
5869         
5870         
5871         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5872             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5873             
5874             // tag can override this..
5875             
5876             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5877         }
5878         
5879         if (this.brand !== '') {
5880             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5881             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5882                 tag: 'a',
5883                 href: this.brand_href ? this.brand_href : '#',
5884                 cls: 'navbar-brand',
5885                 cn: [
5886                 this.brand
5887                 ]
5888             });
5889         }
5890         
5891         if(this.main){
5892             cfg.cls += ' main-nav';
5893         }
5894         
5895         
5896         return cfg;
5897
5898         
5899     },
5900     getHeaderChildContainer : function()
5901     {
5902         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5903             return this.el.select('.navbar-header',true).first();
5904         }
5905         
5906         return this.getChildContainer();
5907     },
5908     
5909     getChildContainer : function()
5910     {
5911          
5912         return this.el.select('.roo-navbar-collapse',true).first();
5913          
5914         
5915     },
5916     
5917     initEvents : function()
5918     {
5919         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5920         
5921         if (this.autohide) {
5922             
5923             var prevScroll = 0;
5924             var ft = this.el;
5925             
5926             Roo.get(document).on('scroll',function(e) {
5927                 var ns = Roo.get(document).getScroll().top;
5928                 var os = prevScroll;
5929                 prevScroll = ns;
5930                 
5931                 if(ns > os){
5932                     ft.removeClass('slideDown');
5933                     ft.addClass('slideUp');
5934                     return;
5935                 }
5936                 ft.removeClass('slideUp');
5937                 ft.addClass('slideDown');
5938                  
5939               
5940           },this);
5941         }
5942     }    
5943     
5944 });
5945
5946
5947
5948  
5949
5950  /*
5951  * - LGPL
5952  *
5953  * navbar
5954  * 
5955  */
5956
5957 /**
5958  * @class Roo.bootstrap.NavSidebar
5959  * @extends Roo.bootstrap.Navbar
5960  * Bootstrap Sidebar class
5961  * 
5962  * @constructor
5963  * Create a new Sidebar
5964  * @param {Object} config The config object
5965  */
5966
5967
5968 Roo.bootstrap.NavSidebar = function(config){
5969     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5970 };
5971
5972 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5973     
5974     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5975     
5976     getAutoCreate : function(){
5977         
5978         
5979         return  {
5980             tag: 'div',
5981             cls: 'sidebar sidebar-nav'
5982         };
5983     
5984         
5985     }
5986     
5987     
5988     
5989 });
5990
5991
5992
5993  
5994
5995  /*
5996  * - LGPL
5997  *
5998  * nav group
5999  * 
6000  */
6001
6002 /**
6003  * @class Roo.bootstrap.NavGroup
6004  * @extends Roo.bootstrap.Component
6005  * Bootstrap NavGroup class
6006  * @cfg {String} align (left|right)
6007  * @cfg {Boolean} inverse
6008  * @cfg {String} type (nav|pills|tab) default nav
6009  * @cfg {String} navId - reference Id for navbar.
6010  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6011  * 
6012  * @constructor
6013  * Create a new nav group
6014  * @param {Object} config The config object
6015  */
6016
6017 Roo.bootstrap.NavGroup = function(config){
6018     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6019     this.navItems = [];
6020    
6021     Roo.bootstrap.NavGroup.register(this);
6022      this.addEvents({
6023         /**
6024              * @event changed
6025              * Fires when the active item changes
6026              * @param {Roo.bootstrap.NavGroup} this
6027              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6028              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6029          */
6030         'changed': true
6031      });
6032     
6033 };
6034
6035 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6036     
6037     align: '',
6038     inverse: false,
6039     form: false,
6040     type: 'nav',
6041     navId : '',
6042     // private
6043     pilltype : true,
6044     
6045     navItems : false, 
6046     
6047     getAutoCreate : function()
6048     {
6049         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6050         
6051         cfg = {
6052             tag : 'ul',
6053             cls: 'nav' 
6054         };
6055         if (Roo.bootstrap.version == 4) {
6056             if (['tabs','pills'].indexOf(this.type) != -1) {
6057                 cfg.cls += ' nav-' + this.type; 
6058             } else {
6059                 // trying to remove so header bar can right align top?
6060                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6061                     // do not use on header bar... 
6062                     cfg.cls += ' navbar-nav';
6063                 }
6064             }
6065             
6066         } else {
6067             if (['tabs','pills'].indexOf(this.type) != -1) {
6068                 cfg.cls += ' nav-' + this.type
6069             } else {
6070                 if (this.type !== 'nav') {
6071                     Roo.log('nav type must be nav/tabs/pills')
6072                 }
6073                 cfg.cls += ' navbar-nav'
6074             }
6075         }
6076         
6077         if (this.parent() && this.parent().sidebar) {
6078             cfg = {
6079                 tag: 'ul',
6080                 cls: 'dashboard-menu sidebar-menu'
6081             };
6082             
6083             return cfg;
6084         }
6085         
6086         if (this.form === true) {
6087             cfg = {
6088                 tag: 'form',
6089                 cls: 'navbar-form form-inline'
6090             };
6091             //nav navbar-right ml-md-auto
6092             if (this.align === 'right') {
6093                 cfg.cls += ' navbar-right ml-md-auto';
6094             } else {
6095                 cfg.cls += ' navbar-left';
6096             }
6097         }
6098         
6099         if (this.align === 'right') {
6100             cfg.cls += ' navbar-right ml-md-auto';
6101         } else {
6102             cfg.cls += ' mr-auto';
6103         }
6104         
6105         if (this.inverse) {
6106             cfg.cls += ' navbar-inverse';
6107             
6108         }
6109         
6110         
6111         return cfg;
6112     },
6113     /**
6114     * sets the active Navigation item
6115     * @param {Roo.bootstrap.NavItem} the new current navitem
6116     */
6117     setActiveItem : function(item)
6118     {
6119         var prev = false;
6120         Roo.each(this.navItems, function(v){
6121             if (v == item) {
6122                 return ;
6123             }
6124             if (v.isActive()) {
6125                 v.setActive(false, true);
6126                 prev = v;
6127                 
6128             }
6129             
6130         });
6131
6132         item.setActive(true, true);
6133         this.fireEvent('changed', this, item, prev);
6134         
6135         
6136     },
6137     /**
6138     * gets the active Navigation item
6139     * @return {Roo.bootstrap.NavItem} the current navitem
6140     */
6141     getActive : function()
6142     {
6143         
6144         var prev = false;
6145         Roo.each(this.navItems, function(v){
6146             
6147             if (v.isActive()) {
6148                 prev = v;
6149                 
6150             }
6151             
6152         });
6153         return prev;
6154     },
6155     
6156     indexOfNav : function()
6157     {
6158         
6159         var prev = false;
6160         Roo.each(this.navItems, function(v,i){
6161             
6162             if (v.isActive()) {
6163                 prev = i;
6164                 
6165             }
6166             
6167         });
6168         return prev;
6169     },
6170     /**
6171     * adds a Navigation item
6172     * @param {Roo.bootstrap.NavItem} the navitem to add
6173     */
6174     addItem : function(cfg)
6175     {
6176         if (this.form && Roo.bootstrap.version == 4) {
6177             cfg.tag = 'div';
6178         }
6179         var cn = new Roo.bootstrap.NavItem(cfg);
6180         this.register(cn);
6181         cn.parentId = this.id;
6182         cn.onRender(this.el, null);
6183         return cn;
6184     },
6185     /**
6186     * register a Navigation item
6187     * @param {Roo.bootstrap.NavItem} the navitem to add
6188     */
6189     register : function(item)
6190     {
6191         this.navItems.push( item);
6192         item.navId = this.navId;
6193     
6194     },
6195     
6196     /**
6197     * clear all the Navigation item
6198     */
6199    
6200     clearAll : function()
6201     {
6202         this.navItems = [];
6203         this.el.dom.innerHTML = '';
6204     },
6205     
6206     getNavItem: function(tabId)
6207     {
6208         var ret = false;
6209         Roo.each(this.navItems, function(e) {
6210             if (e.tabId == tabId) {
6211                ret =  e;
6212                return false;
6213             }
6214             return true;
6215             
6216         });
6217         return ret;
6218     },
6219     
6220     setActiveNext : function()
6221     {
6222         var i = this.indexOfNav(this.getActive());
6223         if (i > this.navItems.length) {
6224             return;
6225         }
6226         this.setActiveItem(this.navItems[i+1]);
6227     },
6228     setActivePrev : function()
6229     {
6230         var i = this.indexOfNav(this.getActive());
6231         if (i  < 1) {
6232             return;
6233         }
6234         this.setActiveItem(this.navItems[i-1]);
6235     },
6236     clearWasActive : function(except) {
6237         Roo.each(this.navItems, function(e) {
6238             if (e.tabId != except.tabId && e.was_active) {
6239                e.was_active = false;
6240                return false;
6241             }
6242             return true;
6243             
6244         });
6245     },
6246     getWasActive : function ()
6247     {
6248         var r = false;
6249         Roo.each(this.navItems, function(e) {
6250             if (e.was_active) {
6251                r = e;
6252                return false;
6253             }
6254             return true;
6255             
6256         });
6257         return r;
6258     }
6259     
6260     
6261 });
6262
6263  
6264 Roo.apply(Roo.bootstrap.NavGroup, {
6265     
6266     groups: {},
6267      /**
6268     * register a Navigation Group
6269     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6270     */
6271     register : function(navgrp)
6272     {
6273         this.groups[navgrp.navId] = navgrp;
6274         
6275     },
6276     /**
6277     * fetch a Navigation Group based on the navigation ID
6278     * @param {string} the navgroup to add
6279     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6280     */
6281     get: function(navId) {
6282         if (typeof(this.groups[navId]) == 'undefined') {
6283             return false;
6284             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6285         }
6286         return this.groups[navId] ;
6287     }
6288     
6289     
6290     
6291 });
6292
6293  /*
6294  * - LGPL
6295  *
6296  * row
6297  * 
6298  */
6299
6300 /**
6301  * @class Roo.bootstrap.NavItem
6302  * @extends Roo.bootstrap.Component
6303  * Bootstrap Navbar.NavItem class
6304  * @cfg {String} href  link to
6305  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6306  * @cfg {Boolean} button_outline show and outlined button
6307  * @cfg {String} html content of button
6308  * @cfg {String} badge text inside badge
6309  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6310  * @cfg {String} glyphicon DEPRICATED - use fa
6311  * @cfg {String} icon DEPRICATED - use fa
6312  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6313  * @cfg {Boolean} active Is item active
6314  * @cfg {Boolean} disabled Is item disabled
6315  * @cfg {String} linkcls  Link Class
6316  * @cfg {Boolean} preventDefault (true | false) default false
6317  * @cfg {String} tabId the tab that this item activates.
6318  * @cfg {String} tagtype (a|span) render as a href or span?
6319  * @cfg {Boolean} animateRef (true|false) link to element default false  
6320   
6321  * @constructor
6322  * Create a new Navbar Item
6323  * @param {Object} config The config object
6324  */
6325 Roo.bootstrap.NavItem = function(config){
6326     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6327     this.addEvents({
6328         // raw events
6329         /**
6330          * @event click
6331          * The raw click event for the entire grid.
6332          * @param {Roo.EventObject} e
6333          */
6334         "click" : true,
6335          /**
6336             * @event changed
6337             * Fires when the active item active state changes
6338             * @param {Roo.bootstrap.NavItem} this
6339             * @param {boolean} state the new state
6340              
6341          */
6342         'changed': true,
6343         /**
6344             * @event scrollto
6345             * Fires when scroll to element
6346             * @param {Roo.bootstrap.NavItem} this
6347             * @param {Object} options
6348             * @param {Roo.EventObject} e
6349              
6350          */
6351         'scrollto': true
6352     });
6353    
6354 };
6355
6356 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6357     
6358     href: false,
6359     html: '',
6360     badge: '',
6361     icon: false,
6362     fa : false,
6363     glyphicon: false,
6364     active: false,
6365     preventDefault : false,
6366     tabId : false,
6367     tagtype : 'a',
6368     tag: 'li',
6369     disabled : false,
6370     animateRef : false,
6371     was_active : false,
6372     button_weight : '',
6373     button_outline : false,
6374     linkcls : '',
6375     navLink: false,
6376     
6377     getAutoCreate : function(){
6378          
6379         var cfg = {
6380             tag: this.tag,
6381             cls: 'nav-item'
6382         };
6383         
6384         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6385         
6386         if (this.active) {
6387             cfg.cls +=  ' active' ;
6388         }
6389         if (this.disabled) {
6390             cfg.cls += ' disabled';
6391         }
6392         
6393         // BS4 only?
6394         if (this.button_weight.length) {
6395             cfg.tag = this.href ? 'a' : 'button';
6396             cfg.html = this.html || '';
6397             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6398             if (this.href) {
6399                 cfg.href = this.href;
6400             }
6401             if (this.fa) {
6402                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6403             } else {
6404                 cfg.cls += " nav-html";
6405             }
6406             
6407             // menu .. should add dropdown-menu class - so no need for carat..
6408             
6409             if (this.badge !== '') {
6410                  
6411                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6412             }
6413             return cfg;
6414         }
6415         
6416         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6417             cfg.cn = [
6418                 {
6419                     tag: this.tagtype,
6420                     href : this.href || "#",
6421                     html: this.html || '',
6422                     cls : ''
6423                 }
6424             ];
6425             if (this.tagtype == 'a') {
6426                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6427         
6428             }
6429             if (this.icon) {
6430                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6431             } else  if (this.fa) {
6432                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6433             } else if(this.glyphicon) {
6434                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6435             } else {
6436                 cfg.cn[0].cls += " nav-html";
6437             }
6438             
6439             if (this.menu) {
6440                 cfg.cn[0].html += " <span class='caret'></span>";
6441              
6442             }
6443             
6444             if (this.badge !== '') {
6445                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6446             }
6447         }
6448         
6449         
6450         
6451         return cfg;
6452     },
6453     onRender : function(ct, position)
6454     {
6455        // Roo.log("Call onRender: " + this.xtype);
6456         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6457             this.tag = 'div';
6458         }
6459         
6460         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6461         this.navLink = this.el.select('.nav-link',true).first();
6462         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6463         return ret;
6464     },
6465       
6466     
6467     initEvents: function() 
6468     {
6469         if (typeof (this.menu) != 'undefined') {
6470             this.menu.parentType = this.xtype;
6471             this.menu.triggerEl = this.el;
6472             this.menu = this.addxtype(Roo.apply({}, this.menu));
6473         }
6474         
6475         this.el.on('click', this.onClick, this);
6476         
6477         //if(this.tagtype == 'span'){
6478         //    this.el.select('span',true).on('click', this.onClick, this);
6479         //}
6480        
6481         // at this point parent should be available..
6482         this.parent().register(this);
6483     },
6484     
6485     onClick : function(e)
6486     {
6487         if (e.getTarget('.dropdown-menu-item')) {
6488             // did you click on a menu itemm.... - then don't trigger onclick..
6489             return;
6490         }
6491         
6492         if(
6493                 this.preventDefault || 
6494                 this.href == '#' 
6495         ){
6496             Roo.log("NavItem - prevent Default?");
6497             e.preventDefault();
6498         }
6499         
6500         if (this.disabled) {
6501             return;
6502         }
6503         
6504         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6505         if (tg && tg.transition) {
6506             Roo.log("waiting for the transitionend");
6507             return;
6508         }
6509         
6510         
6511         
6512         //Roo.log("fire event clicked");
6513         if(this.fireEvent('click', this, e) === false){
6514             return;
6515         };
6516         
6517         if(this.tagtype == 'span'){
6518             return;
6519         }
6520         
6521         //Roo.log(this.href);
6522         var ael = this.el.select('a',true).first();
6523         //Roo.log(ael);
6524         
6525         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6526             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6527             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6528                 return; // ignore... - it's a 'hash' to another page.
6529             }
6530             Roo.log("NavItem - prevent Default?");
6531             e.preventDefault();
6532             this.scrollToElement(e);
6533         }
6534         
6535         
6536         var p =  this.parent();
6537    
6538         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6539             if (typeof(p.setActiveItem) !== 'undefined') {
6540                 p.setActiveItem(this);
6541             }
6542         }
6543         
6544         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6545         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6546             // remove the collapsed menu expand...
6547             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6548         }
6549     },
6550     
6551     isActive: function () {
6552         return this.active
6553     },
6554     setActive : function(state, fire, is_was_active)
6555     {
6556         if (this.active && !state && this.navId) {
6557             this.was_active = true;
6558             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6559             if (nv) {
6560                 nv.clearWasActive(this);
6561             }
6562             
6563         }
6564         this.active = state;
6565         
6566         if (!state ) {
6567             this.el.removeClass('active');
6568             this.navLink ? this.navLink.removeClass('active') : false;
6569         } else if (!this.el.hasClass('active')) {
6570             
6571             this.el.addClass('active');
6572             if (Roo.bootstrap.version == 4 && this.navLink ) {
6573                 this.navLink.addClass('active');
6574             }
6575             
6576         }
6577         if (fire) {
6578             this.fireEvent('changed', this, state);
6579         }
6580         
6581         // show a panel if it's registered and related..
6582         
6583         if (!this.navId || !this.tabId || !state || is_was_active) {
6584             return;
6585         }
6586         
6587         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6588         if (!tg) {
6589             return;
6590         }
6591         var pan = tg.getPanelByName(this.tabId);
6592         if (!pan) {
6593             return;
6594         }
6595         // if we can not flip to new panel - go back to old nav highlight..
6596         if (false == tg.showPanel(pan)) {
6597             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6598             if (nv) {
6599                 var onav = nv.getWasActive();
6600                 if (onav) {
6601                     onav.setActive(true, false, true);
6602                 }
6603             }
6604             
6605         }
6606         
6607         
6608         
6609     },
6610      // this should not be here...
6611     setDisabled : function(state)
6612     {
6613         this.disabled = state;
6614         if (!state ) {
6615             this.el.removeClass('disabled');
6616         } else if (!this.el.hasClass('disabled')) {
6617             this.el.addClass('disabled');
6618         }
6619         
6620     },
6621     
6622     /**
6623      * Fetch the element to display the tooltip on.
6624      * @return {Roo.Element} defaults to this.el
6625      */
6626     tooltipEl : function()
6627     {
6628         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6629     },
6630     
6631     scrollToElement : function(e)
6632     {
6633         var c = document.body;
6634         
6635         /*
6636          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6637          */
6638         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6639             c = document.documentElement;
6640         }
6641         
6642         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6643         
6644         if(!target){
6645             return;
6646         }
6647
6648         var o = target.calcOffsetsTo(c);
6649         
6650         var options = {
6651             target : target,
6652             value : o[1]
6653         };
6654         
6655         this.fireEvent('scrollto', this, options, e);
6656         
6657         Roo.get(c).scrollTo('top', options.value, true);
6658         
6659         return;
6660     },
6661     /**
6662      * Set the HTML (text content) of the item
6663      * @param {string} html  content for the nav item
6664      */
6665     setHtml : function(html)
6666     {
6667         this.html = html;
6668         this.htmlEl.dom.innerHTML = html;
6669         
6670     } 
6671 });
6672  
6673
6674  /*
6675  * - LGPL
6676  *
6677  * sidebar item
6678  *
6679  *  li
6680  *    <span> icon </span>
6681  *    <span> text </span>
6682  *    <span>badge </span>
6683  */
6684
6685 /**
6686  * @class Roo.bootstrap.NavSidebarItem
6687  * @extends Roo.bootstrap.NavItem
6688  * Bootstrap Navbar.NavSidebarItem class
6689  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6690  * {Boolean} open is the menu open
6691  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6692  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6693  * {String} buttonSize (sm|md|lg)the extra classes for the button
6694  * {Boolean} showArrow show arrow next to the text (default true)
6695  * @constructor
6696  * Create a new Navbar Button
6697  * @param {Object} config The config object
6698  */
6699 Roo.bootstrap.NavSidebarItem = function(config){
6700     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6701     this.addEvents({
6702         // raw events
6703         /**
6704          * @event click
6705          * The raw click event for the entire grid.
6706          * @param {Roo.EventObject} e
6707          */
6708         "click" : true,
6709          /**
6710             * @event changed
6711             * Fires when the active item active state changes
6712             * @param {Roo.bootstrap.NavSidebarItem} this
6713             * @param {boolean} state the new state
6714              
6715          */
6716         'changed': true
6717     });
6718    
6719 };
6720
6721 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6722     
6723     badgeWeight : 'default',
6724     
6725     open: false,
6726     
6727     buttonView : false,
6728     
6729     buttonWeight : 'default',
6730     
6731     buttonSize : 'md',
6732     
6733     showArrow : true,
6734     
6735     getAutoCreate : function(){
6736         
6737         
6738         var a = {
6739                 tag: 'a',
6740                 href : this.href || '#',
6741                 cls: '',
6742                 html : '',
6743                 cn : []
6744         };
6745         
6746         if(this.buttonView){
6747             a = {
6748                 tag: 'button',
6749                 href : this.href || '#',
6750                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6751                 html : this.html,
6752                 cn : []
6753             };
6754         }
6755         
6756         var cfg = {
6757             tag: 'li',
6758             cls: '',
6759             cn: [ a ]
6760         };
6761         
6762         if (this.active) {
6763             cfg.cls += ' active';
6764         }
6765         
6766         if (this.disabled) {
6767             cfg.cls += ' disabled';
6768         }
6769         if (this.open) {
6770             cfg.cls += ' open x-open';
6771         }
6772         // left icon..
6773         if (this.glyphicon || this.icon) {
6774             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6775             a.cn.push({ tag : 'i', cls : c }) ;
6776         }
6777         
6778         if(!this.buttonView){
6779             var span = {
6780                 tag: 'span',
6781                 html : this.html || ''
6782             };
6783
6784             a.cn.push(span);
6785             
6786         }
6787         
6788         if (this.badge !== '') {
6789             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6790         }
6791         
6792         if (this.menu) {
6793             
6794             if(this.showArrow){
6795                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6796             }
6797             
6798             a.cls += ' dropdown-toggle treeview' ;
6799         }
6800         
6801         return cfg;
6802     },
6803     
6804     initEvents : function()
6805     { 
6806         if (typeof (this.menu) != 'undefined') {
6807             this.menu.parentType = this.xtype;
6808             this.menu.triggerEl = this.el;
6809             this.menu = this.addxtype(Roo.apply({}, this.menu));
6810         }
6811         
6812         this.el.on('click', this.onClick, this);
6813         
6814         if(this.badge !== ''){
6815             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6816         }
6817         
6818     },
6819     
6820     onClick : function(e)
6821     {
6822         if(this.disabled){
6823             e.preventDefault();
6824             return;
6825         }
6826         
6827         if(this.preventDefault){
6828             e.preventDefault();
6829         }
6830         
6831         this.fireEvent('click', this, e);
6832     },
6833     
6834     disable : function()
6835     {
6836         this.setDisabled(true);
6837     },
6838     
6839     enable : function()
6840     {
6841         this.setDisabled(false);
6842     },
6843     
6844     setDisabled : function(state)
6845     {
6846         if(this.disabled == state){
6847             return;
6848         }
6849         
6850         this.disabled = state;
6851         
6852         if (state) {
6853             this.el.addClass('disabled');
6854             return;
6855         }
6856         
6857         this.el.removeClass('disabled');
6858         
6859         return;
6860     },
6861     
6862     setActive : function(state)
6863     {
6864         if(this.active == state){
6865             return;
6866         }
6867         
6868         this.active = state;
6869         
6870         if (state) {
6871             this.el.addClass('active');
6872             return;
6873         }
6874         
6875         this.el.removeClass('active');
6876         
6877         return;
6878     },
6879     
6880     isActive: function () 
6881     {
6882         return this.active;
6883     },
6884     
6885     setBadge : function(str)
6886     {
6887         if(!this.badgeEl){
6888             return;
6889         }
6890         
6891         this.badgeEl.dom.innerHTML = str;
6892     }
6893     
6894    
6895      
6896  
6897 });
6898  
6899
6900  /*
6901  * - LGPL
6902  *
6903  *  Breadcrumb Nav
6904  * 
6905  */
6906 Roo.namespace('Roo.bootstrap.breadcrumb');
6907
6908
6909 /**
6910  * @class Roo.bootstrap.breadcrumb.Nav
6911  * @extends Roo.bootstrap.Component
6912  * Bootstrap Breadcrumb Nav Class
6913  *  
6914  * @children Roo.bootstrap.breadcrumb.Item
6915  * 
6916  * @constructor
6917  * Create a new breadcrumb.Nav
6918  * @param {Object} config The config object
6919  */
6920
6921
6922 Roo.bootstrap.breadcrumb.Nav = function(config){
6923     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6924     
6925     
6926 };
6927
6928 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6929     
6930     getAutoCreate : function()
6931     {
6932
6933         var cfg = {
6934             tag: 'nav',
6935             cn : [
6936                 {
6937                     tag : 'ol',
6938                     cls : 'breadcrumb'
6939                 }
6940             ]
6941             
6942         };
6943           
6944         return cfg;
6945     },
6946     
6947     initEvents: function()
6948     {
6949         this.olEl = this.el.select('ol',true).first();    
6950     },
6951     getChildContainer : function()
6952     {
6953         return this.olEl;  
6954     }
6955     
6956 });
6957
6958  /*
6959  * - LGPL
6960  *
6961  *  Breadcrumb Item
6962  * 
6963  */
6964
6965
6966 /**
6967  * @class Roo.bootstrap.breadcrumb.Nav
6968  * @extends Roo.bootstrap.Component
6969  * Bootstrap Breadcrumb Nav Class
6970  *  
6971  * @children Roo.bootstrap.breadcrumb.Component
6972  * @cfg {String} html the content of the link.
6973  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6974  * @cfg {Boolean} active is it active
6975
6976  * 
6977  * @constructor
6978  * Create a new breadcrumb.Nav
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.breadcrumb.Item = function(config){
6983     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6984     this.addEvents({
6985         // img events
6986         /**
6987          * @event click
6988          * The img click event for the img.
6989          * @param {Roo.EventObject} e
6990          */
6991         "click" : true
6992     });
6993     
6994 };
6995
6996 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6997     
6998     href: false,
6999     html : '',
7000     
7001     getAutoCreate : function()
7002     {
7003
7004         var cfg = {
7005             tag: 'li',
7006             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7007         };
7008         if (this.href !== false) {
7009             cfg.cn = [{
7010                 tag : 'a',
7011                 href : this.href,
7012                 html : this.html
7013             }];
7014         } else {
7015             cfg.html = this.html;
7016         }
7017         
7018         return cfg;
7019     },
7020     
7021     initEvents: function()
7022     {
7023         if (this.href) {
7024             this.el.select('a', true).first().on('click',this.onClick, this)
7025         }
7026         
7027     },
7028     onClick : function(e)
7029     {
7030         e.preventDefault();
7031         this.fireEvent('click',this,  e);
7032     }
7033     
7034 });
7035
7036  /*
7037  * - LGPL
7038  *
7039  * row
7040  * 
7041  */
7042
7043 /**
7044  * @class Roo.bootstrap.Row
7045  * @extends Roo.bootstrap.Component
7046  * Bootstrap Row class (contains columns...)
7047  * 
7048  * @constructor
7049  * Create a new Row
7050  * @param {Object} config The config object
7051  */
7052
7053 Roo.bootstrap.Row = function(config){
7054     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7055 };
7056
7057 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7058     
7059     getAutoCreate : function(){
7060        return {
7061             cls: 'row clearfix'
7062        };
7063     }
7064     
7065     
7066 });
7067
7068  
7069
7070  /*
7071  * - LGPL
7072  *
7073  * pagination
7074  * 
7075  */
7076
7077 /**
7078  * @class Roo.bootstrap.Pagination
7079  * @extends Roo.bootstrap.Component
7080  * Bootstrap Pagination class
7081  * @cfg {String} size xs | sm | md | lg
7082  * @cfg {Boolean} inverse false | true
7083  * 
7084  * @constructor
7085  * Create a new Pagination
7086  * @param {Object} config The config object
7087  */
7088
7089 Roo.bootstrap.Pagination = function(config){
7090     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7091 };
7092
7093 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7094     
7095     cls: false,
7096     size: false,
7097     inverse: false,
7098     
7099     getAutoCreate : function(){
7100         var cfg = {
7101             tag: 'ul',
7102                 cls: 'pagination'
7103         };
7104         if (this.inverse) {
7105             cfg.cls += ' inverse';
7106         }
7107         if (this.html) {
7108             cfg.html=this.html;
7109         }
7110         if (this.cls) {
7111             cfg.cls += " " + this.cls;
7112         }
7113         return cfg;
7114     }
7115    
7116 });
7117
7118  
7119
7120  /*
7121  * - LGPL
7122  *
7123  * Pagination item
7124  * 
7125  */
7126
7127
7128 /**
7129  * @class Roo.bootstrap.PaginationItem
7130  * @extends Roo.bootstrap.Component
7131  * Bootstrap PaginationItem class
7132  * @cfg {String} html text
7133  * @cfg {String} href the link
7134  * @cfg {Boolean} preventDefault (true | false) default true
7135  * @cfg {Boolean} active (true | false) default false
7136  * @cfg {Boolean} disabled default false
7137  * 
7138  * 
7139  * @constructor
7140  * Create a new PaginationItem
7141  * @param {Object} config The config object
7142  */
7143
7144
7145 Roo.bootstrap.PaginationItem = function(config){
7146     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7147     this.addEvents({
7148         // raw events
7149         /**
7150          * @event click
7151          * The raw click event for the entire grid.
7152          * @param {Roo.EventObject} e
7153          */
7154         "click" : true
7155     });
7156 };
7157
7158 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7159     
7160     href : false,
7161     html : false,
7162     preventDefault: true,
7163     active : false,
7164     cls : false,
7165     disabled: false,
7166     
7167     getAutoCreate : function(){
7168         var cfg= {
7169             tag: 'li',
7170             cn: [
7171                 {
7172                     tag : 'a',
7173                     href : this.href ? this.href : '#',
7174                     html : this.html ? this.html : ''
7175                 }
7176             ]
7177         };
7178         
7179         if(this.cls){
7180             cfg.cls = this.cls;
7181         }
7182         
7183         if(this.disabled){
7184             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7185         }
7186         
7187         if(this.active){
7188             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7189         }
7190         
7191         return cfg;
7192     },
7193     
7194     initEvents: function() {
7195         
7196         this.el.on('click', this.onClick, this);
7197         
7198     },
7199     onClick : function(e)
7200     {
7201         Roo.log('PaginationItem on click ');
7202         if(this.preventDefault){
7203             e.preventDefault();
7204         }
7205         
7206         if(this.disabled){
7207             return;
7208         }
7209         
7210         this.fireEvent('click', this, e);
7211     }
7212    
7213 });
7214
7215  
7216
7217  /*
7218  * - LGPL
7219  *
7220  * slider
7221  * 
7222  */
7223
7224
7225 /**
7226  * @class Roo.bootstrap.Slider
7227  * @extends Roo.bootstrap.Component
7228  * Bootstrap Slider class
7229  *    
7230  * @constructor
7231  * Create a new Slider
7232  * @param {Object} config The config object
7233  */
7234
7235 Roo.bootstrap.Slider = function(config){
7236     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7237 };
7238
7239 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7240     
7241     getAutoCreate : function(){
7242         
7243         var cfg = {
7244             tag: 'div',
7245             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7246             cn: [
7247                 {
7248                     tag: 'a',
7249                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7250                 }
7251             ]
7252         };
7253         
7254         return cfg;
7255     }
7256    
7257 });
7258
7259  /*
7260  * Based on:
7261  * Ext JS Library 1.1.1
7262  * Copyright(c) 2006-2007, Ext JS, LLC.
7263  *
7264  * Originally Released Under LGPL - original licence link has changed is not relivant.
7265  *
7266  * Fork - LGPL
7267  * <script type="text/javascript">
7268  */
7269  
7270
7271 /**
7272  * @class Roo.grid.ColumnModel
7273  * @extends Roo.util.Observable
7274  * This is the default implementation of a ColumnModel used by the Grid. It defines
7275  * the columns in the grid.
7276  * <br>Usage:<br>
7277  <pre><code>
7278  var colModel = new Roo.grid.ColumnModel([
7279         {header: "Ticker", width: 60, sortable: true, locked: true},
7280         {header: "Company Name", width: 150, sortable: true},
7281         {header: "Market Cap.", width: 100, sortable: true},
7282         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7283         {header: "Employees", width: 100, sortable: true, resizable: false}
7284  ]);
7285  </code></pre>
7286  * <p>
7287  
7288  * The config options listed for this class are options which may appear in each
7289  * individual column definition.
7290  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7291  * @constructor
7292  * @param {Object} config An Array of column config objects. See this class's
7293  * config objects for details.
7294 */
7295 Roo.grid.ColumnModel = function(config){
7296         /**
7297      * The config passed into the constructor
7298      */
7299     this.config = config;
7300     this.lookup = {};
7301
7302     // if no id, create one
7303     // if the column does not have a dataIndex mapping,
7304     // map it to the order it is in the config
7305     for(var i = 0, len = config.length; i < len; i++){
7306         var c = config[i];
7307         if(typeof c.dataIndex == "undefined"){
7308             c.dataIndex = i;
7309         }
7310         if(typeof c.renderer == "string"){
7311             c.renderer = Roo.util.Format[c.renderer];
7312         }
7313         if(typeof c.id == "undefined"){
7314             c.id = Roo.id();
7315         }
7316         if(c.editor && c.editor.xtype){
7317             c.editor  = Roo.factory(c.editor, Roo.grid);
7318         }
7319         if(c.editor && c.editor.isFormField){
7320             c.editor = new Roo.grid.GridEditor(c.editor);
7321         }
7322         this.lookup[c.id] = c;
7323     }
7324
7325     /**
7326      * The width of columns which have no width specified (defaults to 100)
7327      * @type Number
7328      */
7329     this.defaultWidth = 100;
7330
7331     /**
7332      * Default sortable of columns which have no sortable specified (defaults to false)
7333      * @type Boolean
7334      */
7335     this.defaultSortable = false;
7336
7337     this.addEvents({
7338         /**
7339              * @event widthchange
7340              * Fires when the width of a column changes.
7341              * @param {ColumnModel} this
7342              * @param {Number} columnIndex The column index
7343              * @param {Number} newWidth The new width
7344              */
7345             "widthchange": true,
7346         /**
7347              * @event headerchange
7348              * Fires when the text of a header changes.
7349              * @param {ColumnModel} this
7350              * @param {Number} columnIndex The column index
7351              * @param {Number} newText The new header text
7352              */
7353             "headerchange": true,
7354         /**
7355              * @event hiddenchange
7356              * Fires when a column is hidden or "unhidden".
7357              * @param {ColumnModel} this
7358              * @param {Number} columnIndex The column index
7359              * @param {Boolean} hidden true if hidden, false otherwise
7360              */
7361             "hiddenchange": true,
7362             /**
7363          * @event columnmoved
7364          * Fires when a column is moved.
7365          * @param {ColumnModel} this
7366          * @param {Number} oldIndex
7367          * @param {Number} newIndex
7368          */
7369         "columnmoved" : true,
7370         /**
7371          * @event columlockchange
7372          * Fires when a column's locked state is changed
7373          * @param {ColumnModel} this
7374          * @param {Number} colIndex
7375          * @param {Boolean} locked true if locked
7376          */
7377         "columnlockchange" : true
7378     });
7379     Roo.grid.ColumnModel.superclass.constructor.call(this);
7380 };
7381 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7382     /**
7383      * @cfg {String} header The header text to display in the Grid view.
7384      */
7385     /**
7386      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7387      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7388      * specified, the column's index is used as an index into the Record's data Array.
7389      */
7390     /**
7391      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7392      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7393      */
7394     /**
7395      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7396      * Defaults to the value of the {@link #defaultSortable} property.
7397      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7398      */
7399     /**
7400      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7401      */
7402     /**
7403      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7404      */
7405     /**
7406      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7407      */
7408     /**
7409      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7410      */
7411     /**
7412      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7413      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7414      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7415      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7416      */
7417        /**
7418      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7419      */
7420     /**
7421      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7422      */
7423     /**
7424      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7425      */
7426     /**
7427      * @cfg {String} cursor (Optional)
7428      */
7429     /**
7430      * @cfg {String} tooltip (Optional)
7431      */
7432     /**
7433      * @cfg {Number} xs (Optional)
7434      */
7435     /**
7436      * @cfg {Number} sm (Optional)
7437      */
7438     /**
7439      * @cfg {Number} md (Optional)
7440      */
7441     /**
7442      * @cfg {Number} lg (Optional)
7443      */
7444     /**
7445      * Returns the id of the column at the specified index.
7446      * @param {Number} index The column index
7447      * @return {String} the id
7448      */
7449     getColumnId : function(index){
7450         return this.config[index].id;
7451     },
7452
7453     /**
7454      * Returns the column for a specified id.
7455      * @param {String} id The column id
7456      * @return {Object} the column
7457      */
7458     getColumnById : function(id){
7459         return this.lookup[id];
7460     },
7461
7462     
7463     /**
7464      * Returns the column for a specified dataIndex.
7465      * @param {String} dataIndex The column dataIndex
7466      * @return {Object|Boolean} the column or false if not found
7467      */
7468     getColumnByDataIndex: function(dataIndex){
7469         var index = this.findColumnIndex(dataIndex);
7470         return index > -1 ? this.config[index] : false;
7471     },
7472     
7473     /**
7474      * Returns the index for a specified column id.
7475      * @param {String} id The column id
7476      * @return {Number} the index, or -1 if not found
7477      */
7478     getIndexById : function(id){
7479         for(var i = 0, len = this.config.length; i < len; i++){
7480             if(this.config[i].id == id){
7481                 return i;
7482             }
7483         }
7484         return -1;
7485     },
7486     
7487     /**
7488      * Returns the index for a specified column dataIndex.
7489      * @param {String} dataIndex The column dataIndex
7490      * @return {Number} the index, or -1 if not found
7491      */
7492     
7493     findColumnIndex : function(dataIndex){
7494         for(var i = 0, len = this.config.length; i < len; i++){
7495             if(this.config[i].dataIndex == dataIndex){
7496                 return i;
7497             }
7498         }
7499         return -1;
7500     },
7501     
7502     
7503     moveColumn : function(oldIndex, newIndex){
7504         var c = this.config[oldIndex];
7505         this.config.splice(oldIndex, 1);
7506         this.config.splice(newIndex, 0, c);
7507         this.dataMap = null;
7508         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7509     },
7510
7511     isLocked : function(colIndex){
7512         return this.config[colIndex].locked === true;
7513     },
7514
7515     setLocked : function(colIndex, value, suppressEvent){
7516         if(this.isLocked(colIndex) == value){
7517             return;
7518         }
7519         this.config[colIndex].locked = value;
7520         if(!suppressEvent){
7521             this.fireEvent("columnlockchange", this, colIndex, value);
7522         }
7523     },
7524
7525     getTotalLockedWidth : function(){
7526         var totalWidth = 0;
7527         for(var i = 0; i < this.config.length; i++){
7528             if(this.isLocked(i) && !this.isHidden(i)){
7529                 this.totalWidth += this.getColumnWidth(i);
7530             }
7531         }
7532         return totalWidth;
7533     },
7534
7535     getLockedCount : function(){
7536         for(var i = 0, len = this.config.length; i < len; i++){
7537             if(!this.isLocked(i)){
7538                 return i;
7539             }
7540         }
7541         
7542         return this.config.length;
7543     },
7544
7545     /**
7546      * Returns the number of columns.
7547      * @return {Number}
7548      */
7549     getColumnCount : function(visibleOnly){
7550         if(visibleOnly === true){
7551             var c = 0;
7552             for(var i = 0, len = this.config.length; i < len; i++){
7553                 if(!this.isHidden(i)){
7554                     c++;
7555                 }
7556             }
7557             return c;
7558         }
7559         return this.config.length;
7560     },
7561
7562     /**
7563      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7564      * @param {Function} fn
7565      * @param {Object} scope (optional)
7566      * @return {Array} result
7567      */
7568     getColumnsBy : function(fn, scope){
7569         var r = [];
7570         for(var i = 0, len = this.config.length; i < len; i++){
7571             var c = this.config[i];
7572             if(fn.call(scope||this, c, i) === true){
7573                 r[r.length] = c;
7574             }
7575         }
7576         return r;
7577     },
7578
7579     /**
7580      * Returns true if the specified column is sortable.
7581      * @param {Number} col The column index
7582      * @return {Boolean}
7583      */
7584     isSortable : function(col){
7585         if(typeof this.config[col].sortable == "undefined"){
7586             return this.defaultSortable;
7587         }
7588         return this.config[col].sortable;
7589     },
7590
7591     /**
7592      * Returns the rendering (formatting) function defined for the column.
7593      * @param {Number} col The column index.
7594      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7595      */
7596     getRenderer : function(col){
7597         if(!this.config[col].renderer){
7598             return Roo.grid.ColumnModel.defaultRenderer;
7599         }
7600         return this.config[col].renderer;
7601     },
7602
7603     /**
7604      * Sets the rendering (formatting) function for a column.
7605      * @param {Number} col The column index
7606      * @param {Function} fn The function to use to process the cell's raw data
7607      * to return HTML markup for the grid view. The render function is called with
7608      * the following parameters:<ul>
7609      * <li>Data value.</li>
7610      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7611      * <li>css A CSS style string to apply to the table cell.</li>
7612      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7613      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7614      * <li>Row index</li>
7615      * <li>Column index</li>
7616      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7617      */
7618     setRenderer : function(col, fn){
7619         this.config[col].renderer = fn;
7620     },
7621
7622     /**
7623      * Returns the width for the specified column.
7624      * @param {Number} col The column index
7625      * @return {Number}
7626      */
7627     getColumnWidth : function(col){
7628         return this.config[col].width * 1 || this.defaultWidth;
7629     },
7630
7631     /**
7632      * Sets the width for a column.
7633      * @param {Number} col The column index
7634      * @param {Number} width The new width
7635      */
7636     setColumnWidth : function(col, width, suppressEvent){
7637         this.config[col].width = width;
7638         this.totalWidth = null;
7639         if(!suppressEvent){
7640              this.fireEvent("widthchange", this, col, width);
7641         }
7642     },
7643
7644     /**
7645      * Returns the total width of all columns.
7646      * @param {Boolean} includeHidden True to include hidden column widths
7647      * @return {Number}
7648      */
7649     getTotalWidth : function(includeHidden){
7650         if(!this.totalWidth){
7651             this.totalWidth = 0;
7652             for(var i = 0, len = this.config.length; i < len; i++){
7653                 if(includeHidden || !this.isHidden(i)){
7654                     this.totalWidth += this.getColumnWidth(i);
7655                 }
7656             }
7657         }
7658         return this.totalWidth;
7659     },
7660
7661     /**
7662      * Returns the header for the specified column.
7663      * @param {Number} col The column index
7664      * @return {String}
7665      */
7666     getColumnHeader : function(col){
7667         return this.config[col].header;
7668     },
7669
7670     /**
7671      * Sets the header for a column.
7672      * @param {Number} col The column index
7673      * @param {String} header The new header
7674      */
7675     setColumnHeader : function(col, header){
7676         this.config[col].header = header;
7677         this.fireEvent("headerchange", this, col, header);
7678     },
7679
7680     /**
7681      * Returns the tooltip for the specified column.
7682      * @param {Number} col The column index
7683      * @return {String}
7684      */
7685     getColumnTooltip : function(col){
7686             return this.config[col].tooltip;
7687     },
7688     /**
7689      * Sets the tooltip for a column.
7690      * @param {Number} col The column index
7691      * @param {String} tooltip The new tooltip
7692      */
7693     setColumnTooltip : function(col, tooltip){
7694             this.config[col].tooltip = tooltip;
7695     },
7696
7697     /**
7698      * Returns the dataIndex for the specified column.
7699      * @param {Number} col The column index
7700      * @return {Number}
7701      */
7702     getDataIndex : function(col){
7703         return this.config[col].dataIndex;
7704     },
7705
7706     /**
7707      * Sets the dataIndex for a column.
7708      * @param {Number} col The column index
7709      * @param {Number} dataIndex The new dataIndex
7710      */
7711     setDataIndex : function(col, dataIndex){
7712         this.config[col].dataIndex = dataIndex;
7713     },
7714
7715     
7716     
7717     /**
7718      * Returns true if the cell is editable.
7719      * @param {Number} colIndex The column index
7720      * @param {Number} rowIndex The row index - this is nto actually used..?
7721      * @return {Boolean}
7722      */
7723     isCellEditable : function(colIndex, rowIndex){
7724         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7725     },
7726
7727     /**
7728      * Returns the editor defined for the cell/column.
7729      * return false or null to disable editing.
7730      * @param {Number} colIndex The column index
7731      * @param {Number} rowIndex The row index
7732      * @return {Object}
7733      */
7734     getCellEditor : function(colIndex, rowIndex){
7735         return this.config[colIndex].editor;
7736     },
7737
7738     /**
7739      * Sets if a column is editable.
7740      * @param {Number} col The column index
7741      * @param {Boolean} editable True if the column is editable
7742      */
7743     setEditable : function(col, editable){
7744         this.config[col].editable = editable;
7745     },
7746
7747
7748     /**
7749      * Returns true if the column is hidden.
7750      * @param {Number} colIndex The column index
7751      * @return {Boolean}
7752      */
7753     isHidden : function(colIndex){
7754         return this.config[colIndex].hidden;
7755     },
7756
7757
7758     /**
7759      * Returns true if the column width cannot be changed
7760      */
7761     isFixed : function(colIndex){
7762         return this.config[colIndex].fixed;
7763     },
7764
7765     /**
7766      * Returns true if the column can be resized
7767      * @return {Boolean}
7768      */
7769     isResizable : function(colIndex){
7770         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7771     },
7772     /**
7773      * Sets if a column is hidden.
7774      * @param {Number} colIndex The column index
7775      * @param {Boolean} hidden True if the column is hidden
7776      */
7777     setHidden : function(colIndex, hidden){
7778         this.config[colIndex].hidden = hidden;
7779         this.totalWidth = null;
7780         this.fireEvent("hiddenchange", this, colIndex, hidden);
7781     },
7782
7783     /**
7784      * Sets the editor for a column.
7785      * @param {Number} col The column index
7786      * @param {Object} editor The editor object
7787      */
7788     setEditor : function(col, editor){
7789         this.config[col].editor = editor;
7790     }
7791 });
7792
7793 Roo.grid.ColumnModel.defaultRenderer = function(value)
7794 {
7795     if(typeof value == "object") {
7796         return value;
7797     }
7798         if(typeof value == "string" && value.length < 1){
7799             return "&#160;";
7800         }
7801     
7802         return String.format("{0}", value);
7803 };
7804
7805 // Alias for backwards compatibility
7806 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7807 /*
7808  * Based on:
7809  * Ext JS Library 1.1.1
7810  * Copyright(c) 2006-2007, Ext JS, LLC.
7811  *
7812  * Originally Released Under LGPL - original licence link has changed is not relivant.
7813  *
7814  * Fork - LGPL
7815  * <script type="text/javascript">
7816  */
7817  
7818 /**
7819  * @class Roo.LoadMask
7820  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7821  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7822  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7823  * element's UpdateManager load indicator and will be destroyed after the initial load.
7824  * @constructor
7825  * Create a new LoadMask
7826  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7827  * @param {Object} config The config object
7828  */
7829 Roo.LoadMask = function(el, config){
7830     this.el = Roo.get(el);
7831     Roo.apply(this, config);
7832     if(this.store){
7833         this.store.on('beforeload', this.onBeforeLoad, this);
7834         this.store.on('load', this.onLoad, this);
7835         this.store.on('loadexception', this.onLoadException, this);
7836         this.removeMask = false;
7837     }else{
7838         var um = this.el.getUpdateManager();
7839         um.showLoadIndicator = false; // disable the default indicator
7840         um.on('beforeupdate', this.onBeforeLoad, this);
7841         um.on('update', this.onLoad, this);
7842         um.on('failure', this.onLoad, this);
7843         this.removeMask = true;
7844     }
7845 };
7846
7847 Roo.LoadMask.prototype = {
7848     /**
7849      * @cfg {Boolean} removeMask
7850      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7851      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7852      */
7853     /**
7854      * @cfg {String} msg
7855      * The text to display in a centered loading message box (defaults to 'Loading...')
7856      */
7857     msg : 'Loading...',
7858     /**
7859      * @cfg {String} msgCls
7860      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7861      */
7862     msgCls : 'x-mask-loading',
7863
7864     /**
7865      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7866      * @type Boolean
7867      */
7868     disabled: false,
7869
7870     /**
7871      * Disables the mask to prevent it from being displayed
7872      */
7873     disable : function(){
7874        this.disabled = true;
7875     },
7876
7877     /**
7878      * Enables the mask so that it can be displayed
7879      */
7880     enable : function(){
7881         this.disabled = false;
7882     },
7883     
7884     onLoadException : function()
7885     {
7886         Roo.log(arguments);
7887         
7888         if (typeof(arguments[3]) != 'undefined') {
7889             Roo.MessageBox.alert("Error loading",arguments[3]);
7890         } 
7891         /*
7892         try {
7893             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7894                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7895             }   
7896         } catch(e) {
7897             
7898         }
7899         */
7900     
7901         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7902     },
7903     // private
7904     onLoad : function()
7905     {
7906         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7907     },
7908
7909     // private
7910     onBeforeLoad : function(){
7911         if(!this.disabled){
7912             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7913         }
7914     },
7915
7916     // private
7917     destroy : function(){
7918         if(this.store){
7919             this.store.un('beforeload', this.onBeforeLoad, this);
7920             this.store.un('load', this.onLoad, this);
7921             this.store.un('loadexception', this.onLoadException, this);
7922         }else{
7923             var um = this.el.getUpdateManager();
7924             um.un('beforeupdate', this.onBeforeLoad, this);
7925             um.un('update', this.onLoad, this);
7926             um.un('failure', this.onLoad, this);
7927         }
7928     }
7929 };/*
7930  * - LGPL
7931  *
7932  * table
7933  * 
7934  */
7935
7936 /**
7937  * @class Roo.bootstrap.Table
7938  * @extends Roo.bootstrap.Component
7939  * Bootstrap Table class
7940  * @cfg {String} cls table class
7941  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7942  * @cfg {String} bgcolor Specifies the background color for a table
7943  * @cfg {Number} border Specifies whether the table cells should have borders or not
7944  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7945  * @cfg {Number} cellspacing Specifies the space between cells
7946  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7947  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7948  * @cfg {String} sortable Specifies that the table should be sortable
7949  * @cfg {String} summary Specifies a summary of the content of a table
7950  * @cfg {Number} width Specifies the width of a table
7951  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7952  * 
7953  * @cfg {boolean} striped Should the rows be alternative striped
7954  * @cfg {boolean} bordered Add borders to the table
7955  * @cfg {boolean} hover Add hover highlighting
7956  * @cfg {boolean} condensed Format condensed
7957  * @cfg {boolean} responsive Format condensed
7958  * @cfg {Boolean} loadMask (true|false) default false
7959  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7960  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7961  * @cfg {Boolean} rowSelection (true|false) default false
7962  * @cfg {Boolean} cellSelection (true|false) default false
7963  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7964  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7965  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7966  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7967  
7968  * 
7969  * @constructor
7970  * Create a new Table
7971  * @param {Object} config The config object
7972  */
7973
7974 Roo.bootstrap.Table = function(config){
7975     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7976     
7977   
7978     
7979     // BC...
7980     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7981     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7982     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7983     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7984     
7985     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7986     if (this.sm) {
7987         this.sm.grid = this;
7988         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7989         this.sm = this.selModel;
7990         this.sm.xmodule = this.xmodule || false;
7991     }
7992     
7993     if (this.cm && typeof(this.cm.config) == 'undefined') {
7994         this.colModel = new Roo.grid.ColumnModel(this.cm);
7995         this.cm = this.colModel;
7996         this.cm.xmodule = this.xmodule || false;
7997     }
7998     if (this.store) {
7999         this.store= Roo.factory(this.store, Roo.data);
8000         this.ds = this.store;
8001         this.ds.xmodule = this.xmodule || false;
8002          
8003     }
8004     if (this.footer && this.store) {
8005         this.footer.dataSource = this.ds;
8006         this.footer = Roo.factory(this.footer);
8007     }
8008     
8009     /** @private */
8010     this.addEvents({
8011         /**
8012          * @event cellclick
8013          * Fires when a cell is clicked
8014          * @param {Roo.bootstrap.Table} this
8015          * @param {Roo.Element} el
8016          * @param {Number} rowIndex
8017          * @param {Number} columnIndex
8018          * @param {Roo.EventObject} e
8019          */
8020         "cellclick" : true,
8021         /**
8022          * @event celldblclick
8023          * Fires when a cell is double clicked
8024          * @param {Roo.bootstrap.Table} this
8025          * @param {Roo.Element} el
8026          * @param {Number} rowIndex
8027          * @param {Number} columnIndex
8028          * @param {Roo.EventObject} e
8029          */
8030         "celldblclick" : true,
8031         /**
8032          * @event rowclick
8033          * Fires when a row is clicked
8034          * @param {Roo.bootstrap.Table} this
8035          * @param {Roo.Element} el
8036          * @param {Number} rowIndex
8037          * @param {Roo.EventObject} e
8038          */
8039         "rowclick" : true,
8040         /**
8041          * @event rowdblclick
8042          * Fires when a row is double clicked
8043          * @param {Roo.bootstrap.Table} this
8044          * @param {Roo.Element} el
8045          * @param {Number} rowIndex
8046          * @param {Roo.EventObject} e
8047          */
8048         "rowdblclick" : true,
8049         /**
8050          * @event mouseover
8051          * Fires when a mouseover occur
8052          * @param {Roo.bootstrap.Table} this
8053          * @param {Roo.Element} el
8054          * @param {Number} rowIndex
8055          * @param {Number} columnIndex
8056          * @param {Roo.EventObject} e
8057          */
8058         "mouseover" : true,
8059         /**
8060          * @event mouseout
8061          * Fires when a mouseout occur
8062          * @param {Roo.bootstrap.Table} this
8063          * @param {Roo.Element} el
8064          * @param {Number} rowIndex
8065          * @param {Number} columnIndex
8066          * @param {Roo.EventObject} e
8067          */
8068         "mouseout" : true,
8069         /**
8070          * @event rowclass
8071          * Fires when a row is rendered, so you can change add a style to it.
8072          * @param {Roo.bootstrap.Table} this
8073          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8074          */
8075         'rowclass' : true,
8076           /**
8077          * @event rowsrendered
8078          * Fires when all the  rows have been rendered
8079          * @param {Roo.bootstrap.Table} this
8080          */
8081         'rowsrendered' : true,
8082         /**
8083          * @event contextmenu
8084          * The raw contextmenu event for the entire grid.
8085          * @param {Roo.EventObject} e
8086          */
8087         "contextmenu" : true,
8088         /**
8089          * @event rowcontextmenu
8090          * Fires when a row is right clicked
8091          * @param {Roo.bootstrap.Table} this
8092          * @param {Number} rowIndex
8093          * @param {Roo.EventObject} e
8094          */
8095         "rowcontextmenu" : true,
8096         /**
8097          * @event cellcontextmenu
8098          * Fires when a cell is right clicked
8099          * @param {Roo.bootstrap.Table} this
8100          * @param {Number} rowIndex
8101          * @param {Number} cellIndex
8102          * @param {Roo.EventObject} e
8103          */
8104          "cellcontextmenu" : true,
8105          /**
8106          * @event headercontextmenu
8107          * Fires when a header is right clicked
8108          * @param {Roo.bootstrap.Table} this
8109          * @param {Number} columnIndex
8110          * @param {Roo.EventObject} e
8111          */
8112         "headercontextmenu" : true
8113     });
8114 };
8115
8116 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8117     
8118     cls: false,
8119     align: false,
8120     bgcolor: false,
8121     border: false,
8122     cellpadding: false,
8123     cellspacing: false,
8124     frame: false,
8125     rules: false,
8126     sortable: false,
8127     summary: false,
8128     width: false,
8129     striped : false,
8130     scrollBody : false,
8131     bordered: false,
8132     hover:  false,
8133     condensed : false,
8134     responsive : false,
8135     sm : false,
8136     cm : false,
8137     store : false,
8138     loadMask : false,
8139     footerShow : true,
8140     headerShow : true,
8141   
8142     rowSelection : false,
8143     cellSelection : false,
8144     layout : false,
8145     
8146     // Roo.Element - the tbody
8147     mainBody: false,
8148     // Roo.Element - thead element
8149     mainHead: false,
8150     
8151     container: false, // used by gridpanel...
8152     
8153     lazyLoad : false,
8154     
8155     CSS : Roo.util.CSS,
8156     
8157     auto_hide_footer : false,
8158     
8159     getAutoCreate : function()
8160     {
8161         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8162         
8163         cfg = {
8164             tag: 'table',
8165             cls : 'table',
8166             cn : []
8167         };
8168         if (this.scrollBody) {
8169             cfg.cls += ' table-body-fixed';
8170         }    
8171         if (this.striped) {
8172             cfg.cls += ' table-striped';
8173         }
8174         
8175         if (this.hover) {
8176             cfg.cls += ' table-hover';
8177         }
8178         if (this.bordered) {
8179             cfg.cls += ' table-bordered';
8180         }
8181         if (this.condensed) {
8182             cfg.cls += ' table-condensed';
8183         }
8184         if (this.responsive) {
8185             cfg.cls += ' table-responsive';
8186         }
8187         
8188         if (this.cls) {
8189             cfg.cls+=  ' ' +this.cls;
8190         }
8191         
8192         // this lot should be simplifed...
8193         var _t = this;
8194         var cp = [
8195             'align',
8196             'bgcolor',
8197             'border',
8198             'cellpadding',
8199             'cellspacing',
8200             'frame',
8201             'rules',
8202             'sortable',
8203             'summary',
8204             'width'
8205         ].forEach(function(k) {
8206             if (_t[k]) {
8207                 cfg[k] = _t[k];
8208             }
8209         });
8210         
8211         
8212         if (this.layout) {
8213             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8214         }
8215         
8216         if(this.store || this.cm){
8217             if(this.headerShow){
8218                 cfg.cn.push(this.renderHeader());
8219             }
8220             
8221             cfg.cn.push(this.renderBody());
8222             
8223             if(this.footerShow){
8224                 cfg.cn.push(this.renderFooter());
8225             }
8226             // where does this come from?
8227             //cfg.cls+=  ' TableGrid';
8228         }
8229         
8230         return { cn : [ cfg ] };
8231     },
8232     
8233     initEvents : function()
8234     {   
8235         if(!this.store || !this.cm){
8236             return;
8237         }
8238         if (this.selModel) {
8239             this.selModel.initEvents();
8240         }
8241         
8242         
8243         //Roo.log('initEvents with ds!!!!');
8244         
8245         this.mainBody = this.el.select('tbody', true).first();
8246         this.mainHead = this.el.select('thead', true).first();
8247         this.mainFoot = this.el.select('tfoot', true).first();
8248         
8249         
8250         
8251         var _this = this;
8252         
8253         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8254             e.on('click', _this.sort, _this);
8255         });
8256         
8257         this.mainBody.on("click", this.onClick, this);
8258         this.mainBody.on("dblclick", this.onDblClick, this);
8259         
8260         // why is this done????? = it breaks dialogs??
8261         //this.parent().el.setStyle('position', 'relative');
8262         
8263         
8264         if (this.footer) {
8265             this.footer.parentId = this.id;
8266             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8267             
8268             if(this.lazyLoad){
8269                 this.el.select('tfoot tr td').first().addClass('hide');
8270             }
8271         } 
8272         
8273         if(this.loadMask) {
8274             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8275         }
8276         
8277         this.store.on('load', this.onLoad, this);
8278         this.store.on('beforeload', this.onBeforeLoad, this);
8279         this.store.on('update', this.onUpdate, this);
8280         this.store.on('add', this.onAdd, this);
8281         this.store.on("clear", this.clear, this);
8282         
8283         this.el.on("contextmenu", this.onContextMenu, this);
8284         
8285         this.mainBody.on('scroll', this.onBodyScroll, this);
8286         
8287         this.cm.on("headerchange", this.onHeaderChange, this);
8288         
8289         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8290         
8291     },
8292     
8293     onContextMenu : function(e, t)
8294     {
8295         this.processEvent("contextmenu", e);
8296     },
8297     
8298     processEvent : function(name, e)
8299     {
8300         if (name != 'touchstart' ) {
8301             this.fireEvent(name, e);    
8302         }
8303         
8304         var t = e.getTarget();
8305         
8306         var cell = Roo.get(t);
8307         
8308         if(!cell){
8309             return;
8310         }
8311         
8312         if(cell.findParent('tfoot', false, true)){
8313             return;
8314         }
8315         
8316         if(cell.findParent('thead', false, true)){
8317             
8318             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8319                 cell = Roo.get(t).findParent('th', false, true);
8320                 if (!cell) {
8321                     Roo.log("failed to find th in thead?");
8322                     Roo.log(e.getTarget());
8323                     return;
8324                 }
8325             }
8326             
8327             var cellIndex = cell.dom.cellIndex;
8328             
8329             var ename = name == 'touchstart' ? 'click' : name;
8330             this.fireEvent("header" + ename, this, cellIndex, e);
8331             
8332             return;
8333         }
8334         
8335         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8336             cell = Roo.get(t).findParent('td', false, true);
8337             if (!cell) {
8338                 Roo.log("failed to find th in tbody?");
8339                 Roo.log(e.getTarget());
8340                 return;
8341             }
8342         }
8343         
8344         var row = cell.findParent('tr', false, true);
8345         var cellIndex = cell.dom.cellIndex;
8346         var rowIndex = row.dom.rowIndex - 1;
8347         
8348         if(row !== false){
8349             
8350             this.fireEvent("row" + name, this, rowIndex, e);
8351             
8352             if(cell !== false){
8353             
8354                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8355             }
8356         }
8357         
8358     },
8359     
8360     onMouseover : function(e, el)
8361     {
8362         var cell = Roo.get(el);
8363         
8364         if(!cell){
8365             return;
8366         }
8367         
8368         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8369             cell = cell.findParent('td', false, true);
8370         }
8371         
8372         var row = cell.findParent('tr', false, true);
8373         var cellIndex = cell.dom.cellIndex;
8374         var rowIndex = row.dom.rowIndex - 1; // start from 0
8375         
8376         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8377         
8378     },
8379     
8380     onMouseout : 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('mouseout', this, cell, rowIndex, cellIndex, e);
8397         
8398     },
8399     
8400     onClick : function(e, el)
8401     {
8402         var cell = Roo.get(el);
8403         
8404         if(!cell || (!this.cellSelection && !this.rowSelection)){
8405             return;
8406         }
8407         
8408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8409             cell = cell.findParent('td', false, true);
8410         }
8411         
8412         if(!cell || typeof(cell) == 'undefined'){
8413             return;
8414         }
8415         
8416         var row = cell.findParent('tr', false, true);
8417         
8418         if(!row || typeof(row) == 'undefined'){
8419             return;
8420         }
8421         
8422         var cellIndex = cell.dom.cellIndex;
8423         var rowIndex = this.getRowIndex(row);
8424         
8425         // why??? - should these not be based on SelectionModel?
8426         if(this.cellSelection){
8427             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8428         }
8429         
8430         if(this.rowSelection){
8431             this.fireEvent('rowclick', this, row, rowIndex, e);
8432         }
8433         
8434         
8435     },
8436         
8437     onDblClick : function(e,el)
8438     {
8439         var cell = Roo.get(el);
8440         
8441         if(!cell || (!this.cellSelection && !this.rowSelection)){
8442             return;
8443         }
8444         
8445         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8446             cell = cell.findParent('td', false, true);
8447         }
8448         
8449         if(!cell || typeof(cell) == 'undefined'){
8450             return;
8451         }
8452         
8453         var row = cell.findParent('tr', false, true);
8454         
8455         if(!row || typeof(row) == 'undefined'){
8456             return;
8457         }
8458         
8459         var cellIndex = cell.dom.cellIndex;
8460         var rowIndex = this.getRowIndex(row);
8461         
8462         if(this.cellSelection){
8463             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8464         }
8465         
8466         if(this.rowSelection){
8467             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8468         }
8469     },
8470     
8471     sort : function(e,el)
8472     {
8473         var col = Roo.get(el);
8474         
8475         if(!col.hasClass('sortable')){
8476             return;
8477         }
8478         
8479         var sort = col.attr('sort');
8480         var dir = 'ASC';
8481         
8482         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8483             dir = 'DESC';
8484         }
8485         
8486         this.store.sortInfo = {field : sort, direction : dir};
8487         
8488         if (this.footer) {
8489             Roo.log("calling footer first");
8490             this.footer.onClick('first');
8491         } else {
8492         
8493             this.store.load({ params : { start : 0 } });
8494         }
8495     },
8496     
8497     renderHeader : function()
8498     {
8499         var header = {
8500             tag: 'thead',
8501             cn : []
8502         };
8503         
8504         var cm = this.cm;
8505         this.totalWidth = 0;
8506         
8507         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8508             
8509             var config = cm.config[i];
8510             
8511             var c = {
8512                 tag: 'th',
8513                 cls : 'x-hcol-' + i,
8514                 style : '',
8515                 html: cm.getColumnHeader(i)
8516             };
8517             
8518             var hh = '';
8519             
8520             if(typeof(config.sortable) != 'undefined' && config.sortable){
8521                 c.cls = 'sortable';
8522                 c.html = '<i class="glyphicon"></i>' + c.html;
8523             }
8524             
8525             // could use BS4 hidden-..-down 
8526             
8527             if(typeof(config.lgHeader) != 'undefined'){
8528                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8529             }
8530             
8531             if(typeof(config.mdHeader) != 'undefined'){
8532                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8533             }
8534             
8535             if(typeof(config.smHeader) != 'undefined'){
8536                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8537             }
8538             
8539             if(typeof(config.xsHeader) != 'undefined'){
8540                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8541             }
8542             
8543             if(hh.length){
8544                 c.html = hh;
8545             }
8546             
8547             if(typeof(config.tooltip) != 'undefined'){
8548                 c.tooltip = config.tooltip;
8549             }
8550             
8551             if(typeof(config.colspan) != 'undefined'){
8552                 c.colspan = config.colspan;
8553             }
8554             
8555             if(typeof(config.hidden) != 'undefined' && config.hidden){
8556                 c.style += ' display:none;';
8557             }
8558             
8559             if(typeof(config.dataIndex) != 'undefined'){
8560                 c.sort = config.dataIndex;
8561             }
8562             
8563            
8564             
8565             if(typeof(config.align) != 'undefined' && config.align.length){
8566                 c.style += ' text-align:' + config.align + ';';
8567             }
8568             
8569             if(typeof(config.width) != 'undefined'){
8570                 c.style += ' width:' + config.width + 'px;';
8571                 this.totalWidth += config.width;
8572             } else {
8573                 this.totalWidth += 100; // assume minimum of 100 per column?
8574             }
8575             
8576             if(typeof(config.cls) != 'undefined'){
8577                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8578             }
8579             
8580             ['xs','sm','md','lg'].map(function(size){
8581                 
8582                 if(typeof(config[size]) == 'undefined'){
8583                     return;
8584                 }
8585                  
8586                 if (!config[size]) { // 0 = hidden
8587                     // BS 4 '0' is treated as hide that column and below.
8588                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8589                     return;
8590                 }
8591                 
8592                 c.cls += ' col-' + size + '-' + config[size] + (
8593                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8594                 );
8595                 
8596                 
8597             });
8598             
8599             header.cn.push(c)
8600         }
8601         
8602         return header;
8603     },
8604     
8605     renderBody : function()
8606     {
8607         var body = {
8608             tag: 'tbody',
8609             cn : [
8610                 {
8611                     tag: 'tr',
8612                     cn : [
8613                         {
8614                             tag : 'td',
8615                             colspan :  this.cm.getColumnCount()
8616                         }
8617                     ]
8618                 }
8619             ]
8620         };
8621         
8622         return body;
8623     },
8624     
8625     renderFooter : function()
8626     {
8627         var footer = {
8628             tag: 'tfoot',
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 footer;
8643     },
8644     
8645     
8646     
8647     onLoad : function()
8648     {
8649 //        Roo.log('ds onload');
8650         this.clear();
8651         
8652         var _this = this;
8653         var cm = this.cm;
8654         var ds = this.store;
8655         
8656         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8657             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8658             if (_this.store.sortInfo) {
8659                     
8660                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8661                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8662                 }
8663                 
8664                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8665                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8666                 }
8667             }
8668         });
8669         
8670         var tbody =  this.mainBody;
8671               
8672         if(ds.getCount() > 0){
8673             ds.data.each(function(d,rowIndex){
8674                 var row =  this.renderRow(cm, ds, rowIndex);
8675                 
8676                 tbody.createChild(row);
8677                 
8678                 var _this = this;
8679                 
8680                 if(row.cellObjects.length){
8681                     Roo.each(row.cellObjects, function(r){
8682                         _this.renderCellObject(r);
8683                     })
8684                 }
8685                 
8686             }, this);
8687         }
8688         
8689         var tfoot = this.el.select('tfoot', true).first();
8690         
8691         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8692             
8693             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8694             
8695             var total = this.ds.getTotalCount();
8696             
8697             if(this.footer.pageSize < total){
8698                 this.mainFoot.show();
8699             }
8700         }
8701         
8702         Roo.each(this.el.select('tbody td', true).elements, function(e){
8703             e.on('mouseover', _this.onMouseover, _this);
8704         });
8705         
8706         Roo.each(this.el.select('tbody td', true).elements, function(e){
8707             e.on('mouseout', _this.onMouseout, _this);
8708         });
8709         this.fireEvent('rowsrendered', this);
8710         
8711         this.autoSize();
8712     },
8713     
8714     
8715     onUpdate : function(ds,record)
8716     {
8717         this.refreshRow(record);
8718         this.autoSize();
8719     },
8720     
8721     onRemove : function(ds, record, index, isUpdate){
8722         if(isUpdate !== true){
8723             this.fireEvent("beforerowremoved", this, index, record);
8724         }
8725         var bt = this.mainBody.dom;
8726         
8727         var rows = this.el.select('tbody > tr', true).elements;
8728         
8729         if(typeof(rows[index]) != 'undefined'){
8730             bt.removeChild(rows[index].dom);
8731         }
8732         
8733 //        if(bt.rows[index]){
8734 //            bt.removeChild(bt.rows[index]);
8735 //        }
8736         
8737         if(isUpdate !== true){
8738             //this.stripeRows(index);
8739             //this.syncRowHeights(index, index);
8740             //this.layout();
8741             this.fireEvent("rowremoved", this, index, record);
8742         }
8743     },
8744     
8745     onAdd : function(ds, records, rowIndex)
8746     {
8747         //Roo.log('on Add called');
8748         // - note this does not handle multiple adding very well..
8749         var bt = this.mainBody.dom;
8750         for (var i =0 ; i < records.length;i++) {
8751             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8752             //Roo.log(records[i]);
8753             //Roo.log(this.store.getAt(rowIndex+i));
8754             this.insertRow(this.store, rowIndex + i, false);
8755             return;
8756         }
8757         
8758     },
8759     
8760     
8761     refreshRow : function(record){
8762         var ds = this.store, index;
8763         if(typeof record == 'number'){
8764             index = record;
8765             record = ds.getAt(index);
8766         }else{
8767             index = ds.indexOf(record);
8768             if (index < 0) {
8769                 return; // should not happen - but seems to 
8770             }
8771         }
8772         this.insertRow(ds, index, true);
8773         this.autoSize();
8774         this.onRemove(ds, record, index+1, true);
8775         this.autoSize();
8776         //this.syncRowHeights(index, index);
8777         //this.layout();
8778         this.fireEvent("rowupdated", this, index, record);
8779     },
8780     
8781     insertRow : function(dm, rowIndex, isUpdate){
8782         
8783         if(!isUpdate){
8784             this.fireEvent("beforerowsinserted", this, rowIndex);
8785         }
8786             //var s = this.getScrollState();
8787         var row = this.renderRow(this.cm, this.store, rowIndex);
8788         // insert before rowIndex..
8789         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8790         
8791         var _this = this;
8792                 
8793         if(row.cellObjects.length){
8794             Roo.each(row.cellObjects, function(r){
8795                 _this.renderCellObject(r);
8796             })
8797         }
8798             
8799         if(!isUpdate){
8800             this.fireEvent("rowsinserted", this, rowIndex);
8801             //this.syncRowHeights(firstRow, lastRow);
8802             //this.stripeRows(firstRow);
8803             //this.layout();
8804         }
8805         
8806     },
8807     
8808     
8809     getRowDom : function(rowIndex)
8810     {
8811         var rows = this.el.select('tbody > tr', true).elements;
8812         
8813         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8814         
8815     },
8816     // returns the object tree for a tr..
8817   
8818     
8819     renderRow : function(cm, ds, rowIndex) 
8820     {
8821         var d = ds.getAt(rowIndex);
8822         
8823         var row = {
8824             tag : 'tr',
8825             cls : 'x-row-' + rowIndex,
8826             cn : []
8827         };
8828             
8829         var cellObjects = [];
8830         
8831         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8832             var config = cm.config[i];
8833             
8834             var renderer = cm.getRenderer(i);
8835             var value = '';
8836             var id = false;
8837             
8838             if(typeof(renderer) !== 'undefined'){
8839                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8840             }
8841             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8842             // and are rendered into the cells after the row is rendered - using the id for the element.
8843             
8844             if(typeof(value) === 'object'){
8845                 id = Roo.id();
8846                 cellObjects.push({
8847                     container : id,
8848                     cfg : value 
8849                 })
8850             }
8851             
8852             var rowcfg = {
8853                 record: d,
8854                 rowIndex : rowIndex,
8855                 colIndex : i,
8856                 rowClass : ''
8857             };
8858
8859             this.fireEvent('rowclass', this, rowcfg);
8860             
8861             var td = {
8862                 tag: 'td',
8863                 cls : rowcfg.rowClass + ' x-col-' + i,
8864                 style: '',
8865                 html: (typeof(value) === 'object') ? '' : value
8866             };
8867             
8868             if (id) {
8869                 td.id = id;
8870             }
8871             
8872             if(typeof(config.colspan) != 'undefined'){
8873                 td.colspan = config.colspan;
8874             }
8875             
8876             if(typeof(config.hidden) != 'undefined' && config.hidden){
8877                 td.style += ' display:none;';
8878             }
8879             
8880             if(typeof(config.align) != 'undefined' && config.align.length){
8881                 td.style += ' text-align:' + config.align + ';';
8882             }
8883             if(typeof(config.valign) != 'undefined' && config.valign.length){
8884                 td.style += ' vertical-align:' + config.valign + ';';
8885             }
8886             
8887             if(typeof(config.width) != 'undefined'){
8888                 td.style += ' width:' +  config.width + 'px;';
8889             }
8890             
8891             if(typeof(config.cursor) != 'undefined'){
8892                 td.style += ' cursor:' +  config.cursor + ';';
8893             }
8894             
8895             if(typeof(config.cls) != 'undefined'){
8896                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8897             }
8898             
8899             ['xs','sm','md','lg'].map(function(size){
8900                 
8901                 if(typeof(config[size]) == 'undefined'){
8902                     return;
8903                 }
8904                 
8905                 
8906                   
8907                 if (!config[size]) { // 0 = hidden
8908                     // BS 4 '0' is treated as hide that column and below.
8909                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8910                     return;
8911                 }
8912                 
8913                 td.cls += ' col-' + size + '-' + config[size] + (
8914                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8915                 );
8916                  
8917
8918             });
8919             
8920             row.cn.push(td);
8921            
8922         }
8923         
8924         row.cellObjects = cellObjects;
8925         
8926         return row;
8927           
8928     },
8929     
8930     
8931     
8932     onBeforeLoad : function()
8933     {
8934         
8935     },
8936      /**
8937      * Remove all rows
8938      */
8939     clear : function()
8940     {
8941         this.el.select('tbody', true).first().dom.innerHTML = '';
8942     },
8943     /**
8944      * Show or hide a row.
8945      * @param {Number} rowIndex to show or hide
8946      * @param {Boolean} state hide
8947      */
8948     setRowVisibility : function(rowIndex, state)
8949     {
8950         var bt = this.mainBody.dom;
8951         
8952         var rows = this.el.select('tbody > tr', true).elements;
8953         
8954         if(typeof(rows[rowIndex]) == 'undefined'){
8955             return;
8956         }
8957         rows[rowIndex].dom.style.display = state ? '' : 'none';
8958     },
8959     
8960     
8961     getSelectionModel : function(){
8962         if(!this.selModel){
8963             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8964         }
8965         return this.selModel;
8966     },
8967     /*
8968      * Render the Roo.bootstrap object from renderder
8969      */
8970     renderCellObject : function(r)
8971     {
8972         var _this = this;
8973         
8974         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8975         
8976         var t = r.cfg.render(r.container);
8977         
8978         if(r.cfg.cn){
8979             Roo.each(r.cfg.cn, function(c){
8980                 var child = {
8981                     container: t.getChildContainer(),
8982                     cfg: c
8983                 };
8984                 _this.renderCellObject(child);
8985             })
8986         }
8987     },
8988     
8989     getRowIndex : function(row)
8990     {
8991         var rowIndex = -1;
8992         
8993         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8994             if(el != row){
8995                 return;
8996             }
8997             
8998             rowIndex = index;
8999         });
9000         
9001         return rowIndex;
9002     },
9003      /**
9004      * Returns the grid's underlying element = used by panel.Grid
9005      * @return {Element} The element
9006      */
9007     getGridEl : function(){
9008         return this.el;
9009     },
9010      /**
9011      * Forces a resize - used by panel.Grid
9012      * @return {Element} The element
9013      */
9014     autoSize : function()
9015     {
9016         //var ctr = Roo.get(this.container.dom.parentElement);
9017         var ctr = Roo.get(this.el.dom);
9018         
9019         var thd = this.getGridEl().select('thead',true).first();
9020         var tbd = this.getGridEl().select('tbody', true).first();
9021         var tfd = this.getGridEl().select('tfoot', true).first();
9022         
9023         var cw = ctr.getWidth();
9024         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9025         
9026         if (tbd) {
9027             
9028             tbd.setWidth(ctr.getWidth());
9029             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9030             // this needs fixing for various usage - currently only hydra job advers I think..
9031             //tdb.setHeight(
9032             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9033             //); 
9034             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9035             cw -= barsize;
9036         }
9037         cw = Math.max(cw, this.totalWidth);
9038         this.getGridEl().select('tbody tr',true).setWidth(cw);
9039         
9040         // resize 'expandable coloumn?
9041         
9042         return; // we doe not have a view in this design..
9043         
9044     },
9045     onBodyScroll: function()
9046     {
9047         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9048         if(this.mainHead){
9049             this.mainHead.setStyle({
9050                 'position' : 'relative',
9051                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9052             });
9053         }
9054         
9055         if(this.lazyLoad){
9056             
9057             var scrollHeight = this.mainBody.dom.scrollHeight;
9058             
9059             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9060             
9061             var height = this.mainBody.getHeight();
9062             
9063             if(scrollHeight - height == scrollTop) {
9064                 
9065                 var total = this.ds.getTotalCount();
9066                 
9067                 if(this.footer.cursor + this.footer.pageSize < total){
9068                     
9069                     this.footer.ds.load({
9070                         params : {
9071                             start : this.footer.cursor + this.footer.pageSize,
9072                             limit : this.footer.pageSize
9073                         },
9074                         add : true
9075                     });
9076                 }
9077             }
9078             
9079         }
9080     },
9081     
9082     onHeaderChange : function()
9083     {
9084         var header = this.renderHeader();
9085         var table = this.el.select('table', true).first();
9086         
9087         this.mainHead.remove();
9088         this.mainHead = table.createChild(header, this.mainBody, false);
9089     },
9090     
9091     onHiddenChange : function(colModel, colIndex, hidden)
9092     {
9093         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9094         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9095         
9096         this.CSS.updateRule(thSelector, "display", "");
9097         this.CSS.updateRule(tdSelector, "display", "");
9098         
9099         if(hidden){
9100             this.CSS.updateRule(thSelector, "display", "none");
9101             this.CSS.updateRule(tdSelector, "display", "none");
9102         }
9103         
9104         this.onHeaderChange();
9105         this.onLoad();
9106     },
9107     
9108     setColumnWidth: function(col_index, width)
9109     {
9110         // width = "md-2 xs-2..."
9111         if(!this.colModel.config[col_index]) {
9112             return;
9113         }
9114         
9115         var w = width.split(" ");
9116         
9117         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9118         
9119         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9120         
9121         
9122         for(var j = 0; j < w.length; j++) {
9123             
9124             if(!w[j]) {
9125                 continue;
9126             }
9127             
9128             var size_cls = w[j].split("-");
9129             
9130             if(!Number.isInteger(size_cls[1] * 1)) {
9131                 continue;
9132             }
9133             
9134             if(!this.colModel.config[col_index][size_cls[0]]) {
9135                 continue;
9136             }
9137             
9138             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9139                 continue;
9140             }
9141             
9142             h_row[0].classList.replace(
9143                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9144                 "col-"+size_cls[0]+"-"+size_cls[1]
9145             );
9146             
9147             for(var i = 0; i < rows.length; i++) {
9148                 
9149                 var size_cls = w[j].split("-");
9150                 
9151                 if(!Number.isInteger(size_cls[1] * 1)) {
9152                     continue;
9153                 }
9154                 
9155                 if(!this.colModel.config[col_index][size_cls[0]]) {
9156                     continue;
9157                 }
9158                 
9159                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9160                     continue;
9161                 }
9162                 
9163                 rows[i].classList.replace(
9164                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9165                     "col-"+size_cls[0]+"-"+size_cls[1]
9166                 );
9167             }
9168             
9169             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9170         }
9171     }
9172 });
9173
9174  
9175
9176  /*
9177  * - LGPL
9178  *
9179  * table cell
9180  * 
9181  */
9182
9183 /**
9184  * @class Roo.bootstrap.TableCell
9185  * @extends Roo.bootstrap.Component
9186  * Bootstrap TableCell class
9187  * @cfg {String} html cell contain text
9188  * @cfg {String} cls cell class
9189  * @cfg {String} tag cell tag (td|th) default td
9190  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9191  * @cfg {String} align Aligns the content in a cell
9192  * @cfg {String} axis Categorizes cells
9193  * @cfg {String} bgcolor Specifies the background color of a cell
9194  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9195  * @cfg {Number} colspan Specifies the number of columns a cell should span
9196  * @cfg {String} headers Specifies one or more header cells a cell is related to
9197  * @cfg {Number} height Sets the height of a cell
9198  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9199  * @cfg {Number} rowspan Sets the number of rows a cell should span
9200  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9201  * @cfg {String} valign Vertical aligns the content in a cell
9202  * @cfg {Number} width Specifies the width of a cell
9203  * 
9204  * @constructor
9205  * Create a new TableCell
9206  * @param {Object} config The config object
9207  */
9208
9209 Roo.bootstrap.TableCell = function(config){
9210     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9211 };
9212
9213 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9214     
9215     html: false,
9216     cls: false,
9217     tag: false,
9218     abbr: false,
9219     align: false,
9220     axis: false,
9221     bgcolor: false,
9222     charoff: false,
9223     colspan: false,
9224     headers: false,
9225     height: false,
9226     nowrap: false,
9227     rowspan: false,
9228     scope: false,
9229     valign: false,
9230     width: false,
9231     
9232     
9233     getAutoCreate : function(){
9234         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9235         
9236         cfg = {
9237             tag: 'td'
9238         };
9239         
9240         if(this.tag){
9241             cfg.tag = this.tag;
9242         }
9243         
9244         if (this.html) {
9245             cfg.html=this.html
9246         }
9247         if (this.cls) {
9248             cfg.cls=this.cls
9249         }
9250         if (this.abbr) {
9251             cfg.abbr=this.abbr
9252         }
9253         if (this.align) {
9254             cfg.align=this.align
9255         }
9256         if (this.axis) {
9257             cfg.axis=this.axis
9258         }
9259         if (this.bgcolor) {
9260             cfg.bgcolor=this.bgcolor
9261         }
9262         if (this.charoff) {
9263             cfg.charoff=this.charoff
9264         }
9265         if (this.colspan) {
9266             cfg.colspan=this.colspan
9267         }
9268         if (this.headers) {
9269             cfg.headers=this.headers
9270         }
9271         if (this.height) {
9272             cfg.height=this.height
9273         }
9274         if (this.nowrap) {
9275             cfg.nowrap=this.nowrap
9276         }
9277         if (this.rowspan) {
9278             cfg.rowspan=this.rowspan
9279         }
9280         if (this.scope) {
9281             cfg.scope=this.scope
9282         }
9283         if (this.valign) {
9284             cfg.valign=this.valign
9285         }
9286         if (this.width) {
9287             cfg.width=this.width
9288         }
9289         
9290         
9291         return cfg;
9292     }
9293    
9294 });
9295
9296  
9297
9298  /*
9299  * - LGPL
9300  *
9301  * table row
9302  * 
9303  */
9304
9305 /**
9306  * @class Roo.bootstrap.TableRow
9307  * @extends Roo.bootstrap.Component
9308  * Bootstrap TableRow class
9309  * @cfg {String} cls row class
9310  * @cfg {String} align Aligns the content in a table row
9311  * @cfg {String} bgcolor Specifies a background color for a table row
9312  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9313  * @cfg {String} valign Vertical aligns the content in a table row
9314  * 
9315  * @constructor
9316  * Create a new TableRow
9317  * @param {Object} config The config object
9318  */
9319
9320 Roo.bootstrap.TableRow = function(config){
9321     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9322 };
9323
9324 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9325     
9326     cls: false,
9327     align: false,
9328     bgcolor: false,
9329     charoff: false,
9330     valign: false,
9331     
9332     getAutoCreate : function(){
9333         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9334         
9335         cfg = {
9336             tag: 'tr'
9337         };
9338             
9339         if(this.cls){
9340             cfg.cls = this.cls;
9341         }
9342         if(this.align){
9343             cfg.align = this.align;
9344         }
9345         if(this.bgcolor){
9346             cfg.bgcolor = this.bgcolor;
9347         }
9348         if(this.charoff){
9349             cfg.charoff = this.charoff;
9350         }
9351         if(this.valign){
9352             cfg.valign = this.valign;
9353         }
9354         
9355         return cfg;
9356     }
9357    
9358 });
9359
9360  
9361
9362  /*
9363  * - LGPL
9364  *
9365  * table body
9366  * 
9367  */
9368
9369 /**
9370  * @class Roo.bootstrap.TableBody
9371  * @extends Roo.bootstrap.Component
9372  * Bootstrap TableBody class
9373  * @cfg {String} cls element class
9374  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9375  * @cfg {String} align Aligns the content inside the element
9376  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9377  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9378  * 
9379  * @constructor
9380  * Create a new TableBody
9381  * @param {Object} config The config object
9382  */
9383
9384 Roo.bootstrap.TableBody = function(config){
9385     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9386 };
9387
9388 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9389     
9390     cls: false,
9391     tag: false,
9392     align: false,
9393     charoff: false,
9394     valign: false,
9395     
9396     getAutoCreate : function(){
9397         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9398         
9399         cfg = {
9400             tag: 'tbody'
9401         };
9402             
9403         if (this.cls) {
9404             cfg.cls=this.cls
9405         }
9406         if(this.tag){
9407             cfg.tag = this.tag;
9408         }
9409         
9410         if(this.align){
9411             cfg.align = this.align;
9412         }
9413         if(this.charoff){
9414             cfg.charoff = this.charoff;
9415         }
9416         if(this.valign){
9417             cfg.valign = this.valign;
9418         }
9419         
9420         return cfg;
9421     }
9422     
9423     
9424 //    initEvents : function()
9425 //    {
9426 //        
9427 //        if(!this.store){
9428 //            return;
9429 //        }
9430 //        
9431 //        this.store = Roo.factory(this.store, Roo.data);
9432 //        this.store.on('load', this.onLoad, this);
9433 //        
9434 //        this.store.load();
9435 //        
9436 //    },
9437 //    
9438 //    onLoad: function () 
9439 //    {   
9440 //        this.fireEvent('load', this);
9441 //    }
9442 //    
9443 //   
9444 });
9445
9446  
9447
9448  /*
9449  * Based on:
9450  * Ext JS Library 1.1.1
9451  * Copyright(c) 2006-2007, Ext JS, LLC.
9452  *
9453  * Originally Released Under LGPL - original licence link has changed is not relivant.
9454  *
9455  * Fork - LGPL
9456  * <script type="text/javascript">
9457  */
9458
9459 // as we use this in bootstrap.
9460 Roo.namespace('Roo.form');
9461  /**
9462  * @class Roo.form.Action
9463  * Internal Class used to handle form actions
9464  * @constructor
9465  * @param {Roo.form.BasicForm} el The form element or its id
9466  * @param {Object} config Configuration options
9467  */
9468
9469  
9470  
9471 // define the action interface
9472 Roo.form.Action = function(form, options){
9473     this.form = form;
9474     this.options = options || {};
9475 };
9476 /**
9477  * Client Validation Failed
9478  * @const 
9479  */
9480 Roo.form.Action.CLIENT_INVALID = 'client';
9481 /**
9482  * Server Validation Failed
9483  * @const 
9484  */
9485 Roo.form.Action.SERVER_INVALID = 'server';
9486  /**
9487  * Connect to Server Failed
9488  * @const 
9489  */
9490 Roo.form.Action.CONNECT_FAILURE = 'connect';
9491 /**
9492  * Reading Data from Server Failed
9493  * @const 
9494  */
9495 Roo.form.Action.LOAD_FAILURE = 'load';
9496
9497 Roo.form.Action.prototype = {
9498     type : 'default',
9499     failureType : undefined,
9500     response : undefined,
9501     result : undefined,
9502
9503     // interface method
9504     run : function(options){
9505
9506     },
9507
9508     // interface method
9509     success : function(response){
9510
9511     },
9512
9513     // interface method
9514     handleResponse : function(response){
9515
9516     },
9517
9518     // default connection failure
9519     failure : function(response){
9520         
9521         this.response = response;
9522         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9523         this.form.afterAction(this, false);
9524     },
9525
9526     processResponse : function(response){
9527         this.response = response;
9528         if(!response.responseText){
9529             return true;
9530         }
9531         this.result = this.handleResponse(response);
9532         return this.result;
9533     },
9534
9535     // utility functions used internally
9536     getUrl : function(appendParams){
9537         var url = this.options.url || this.form.url || this.form.el.dom.action;
9538         if(appendParams){
9539             var p = this.getParams();
9540             if(p){
9541                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9542             }
9543         }
9544         return url;
9545     },
9546
9547     getMethod : function(){
9548         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9549     },
9550
9551     getParams : function(){
9552         var bp = this.form.baseParams;
9553         var p = this.options.params;
9554         if(p){
9555             if(typeof p == "object"){
9556                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9557             }else if(typeof p == 'string' && bp){
9558                 p += '&' + Roo.urlEncode(bp);
9559             }
9560         }else if(bp){
9561             p = Roo.urlEncode(bp);
9562         }
9563         return p;
9564     },
9565
9566     createCallback : function(){
9567         return {
9568             success: this.success,
9569             failure: this.failure,
9570             scope: this,
9571             timeout: (this.form.timeout*1000),
9572             upload: this.form.fileUpload ? this.success : undefined
9573         };
9574     }
9575 };
9576
9577 Roo.form.Action.Submit = function(form, options){
9578     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9579 };
9580
9581 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9582     type : 'submit',
9583
9584     haveProgress : false,
9585     uploadComplete : false,
9586     
9587     // uploadProgress indicator.
9588     uploadProgress : function()
9589     {
9590         if (!this.form.progressUrl) {
9591             return;
9592         }
9593         
9594         if (!this.haveProgress) {
9595             Roo.MessageBox.progress("Uploading", "Uploading");
9596         }
9597         if (this.uploadComplete) {
9598            Roo.MessageBox.hide();
9599            return;
9600         }
9601         
9602         this.haveProgress = true;
9603    
9604         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9605         
9606         var c = new Roo.data.Connection();
9607         c.request({
9608             url : this.form.progressUrl,
9609             params: {
9610                 id : uid
9611             },
9612             method: 'GET',
9613             success : function(req){
9614                //console.log(data);
9615                 var rdata = false;
9616                 var edata;
9617                 try  {
9618                    rdata = Roo.decode(req.responseText)
9619                 } catch (e) {
9620                     Roo.log("Invalid data from server..");
9621                     Roo.log(edata);
9622                     return;
9623                 }
9624                 if (!rdata || !rdata.success) {
9625                     Roo.log(rdata);
9626                     Roo.MessageBox.alert(Roo.encode(rdata));
9627                     return;
9628                 }
9629                 var data = rdata.data;
9630                 
9631                 if (this.uploadComplete) {
9632                    Roo.MessageBox.hide();
9633                    return;
9634                 }
9635                    
9636                 if (data){
9637                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9638                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9639                     );
9640                 }
9641                 this.uploadProgress.defer(2000,this);
9642             },
9643        
9644             failure: function(data) {
9645                 Roo.log('progress url failed ');
9646                 Roo.log(data);
9647             },
9648             scope : this
9649         });
9650            
9651     },
9652     
9653     
9654     run : function()
9655     {
9656         // run get Values on the form, so it syncs any secondary forms.
9657         this.form.getValues();
9658         
9659         var o = this.options;
9660         var method = this.getMethod();
9661         var isPost = method == 'POST';
9662         if(o.clientValidation === false || this.form.isValid()){
9663             
9664             if (this.form.progressUrl) {
9665                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9666                     (new Date() * 1) + '' + Math.random());
9667                     
9668             } 
9669             
9670             
9671             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9672                 form:this.form.el.dom,
9673                 url:this.getUrl(!isPost),
9674                 method: method,
9675                 params:isPost ? this.getParams() : null,
9676                 isUpload: this.form.fileUpload,
9677                 formData : this.form.formData
9678             }));
9679             
9680             this.uploadProgress();
9681
9682         }else if (o.clientValidation !== false){ // client validation failed
9683             this.failureType = Roo.form.Action.CLIENT_INVALID;
9684             this.form.afterAction(this, false);
9685         }
9686     },
9687
9688     success : function(response)
9689     {
9690         this.uploadComplete= true;
9691         if (this.haveProgress) {
9692             Roo.MessageBox.hide();
9693         }
9694         
9695         
9696         var result = this.processResponse(response);
9697         if(result === true || result.success){
9698             this.form.afterAction(this, true);
9699             return;
9700         }
9701         if(result.errors){
9702             this.form.markInvalid(result.errors);
9703             this.failureType = Roo.form.Action.SERVER_INVALID;
9704         }
9705         this.form.afterAction(this, false);
9706     },
9707     failure : function(response)
9708     {
9709         this.uploadComplete= true;
9710         if (this.haveProgress) {
9711             Roo.MessageBox.hide();
9712         }
9713         
9714         this.response = response;
9715         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9716         this.form.afterAction(this, false);
9717     },
9718     
9719     handleResponse : function(response){
9720         if(this.form.errorReader){
9721             var rs = this.form.errorReader.read(response);
9722             var errors = [];
9723             if(rs.records){
9724                 for(var i = 0, len = rs.records.length; i < len; i++) {
9725                     var r = rs.records[i];
9726                     errors[i] = r.data;
9727                 }
9728             }
9729             if(errors.length < 1){
9730                 errors = null;
9731             }
9732             return {
9733                 success : rs.success,
9734                 errors : errors
9735             };
9736         }
9737         var ret = false;
9738         try {
9739             ret = Roo.decode(response.responseText);
9740         } catch (e) {
9741             ret = {
9742                 success: false,
9743                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9744                 errors : []
9745             };
9746         }
9747         return ret;
9748         
9749     }
9750 });
9751
9752
9753 Roo.form.Action.Load = function(form, options){
9754     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9755     this.reader = this.form.reader;
9756 };
9757
9758 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9759     type : 'load',
9760
9761     run : function(){
9762         
9763         Roo.Ajax.request(Roo.apply(
9764                 this.createCallback(), {
9765                     method:this.getMethod(),
9766                     url:this.getUrl(false),
9767                     params:this.getParams()
9768         }));
9769     },
9770
9771     success : function(response){
9772         
9773         var result = this.processResponse(response);
9774         if(result === true || !result.success || !result.data){
9775             this.failureType = Roo.form.Action.LOAD_FAILURE;
9776             this.form.afterAction(this, false);
9777             return;
9778         }
9779         this.form.clearInvalid();
9780         this.form.setValues(result.data);
9781         this.form.afterAction(this, true);
9782     },
9783
9784     handleResponse : function(response){
9785         if(this.form.reader){
9786             var rs = this.form.reader.read(response);
9787             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9788             return {
9789                 success : rs.success,
9790                 data : data
9791             };
9792         }
9793         return Roo.decode(response.responseText);
9794     }
9795 });
9796
9797 Roo.form.Action.ACTION_TYPES = {
9798     'load' : Roo.form.Action.Load,
9799     'submit' : Roo.form.Action.Submit
9800 };/*
9801  * - LGPL
9802  *
9803  * form
9804  *
9805  */
9806
9807 /**
9808  * @class Roo.bootstrap.Form
9809  * @extends Roo.bootstrap.Component
9810  * Bootstrap Form class
9811  * @cfg {String} method  GET | POST (default POST)
9812  * @cfg {String} labelAlign top | left (default top)
9813  * @cfg {String} align left  | right - for navbars
9814  * @cfg {Boolean} loadMask load mask when submit (default true)
9815
9816  *
9817  * @constructor
9818  * Create a new Form
9819  * @param {Object} config The config object
9820  */
9821
9822
9823 Roo.bootstrap.Form = function(config){
9824     
9825     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9826     
9827     Roo.bootstrap.Form.popover.apply();
9828     
9829     this.addEvents({
9830         /**
9831          * @event clientvalidation
9832          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9833          * @param {Form} this
9834          * @param {Boolean} valid true if the form has passed client-side validation
9835          */
9836         clientvalidation: true,
9837         /**
9838          * @event beforeaction
9839          * Fires before any action is performed. Return false to cancel the action.
9840          * @param {Form} this
9841          * @param {Action} action The action to be performed
9842          */
9843         beforeaction: true,
9844         /**
9845          * @event actionfailed
9846          * Fires when an action fails.
9847          * @param {Form} this
9848          * @param {Action} action The action that failed
9849          */
9850         actionfailed : true,
9851         /**
9852          * @event actioncomplete
9853          * Fires when an action is completed.
9854          * @param {Form} this
9855          * @param {Action} action The action that completed
9856          */
9857         actioncomplete : true
9858     });
9859 };
9860
9861 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9862
9863      /**
9864      * @cfg {String} method
9865      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9866      */
9867     method : 'POST',
9868     /**
9869      * @cfg {String} url
9870      * The URL to use for form actions if one isn't supplied in the action options.
9871      */
9872     /**
9873      * @cfg {Boolean} fileUpload
9874      * Set to true if this form is a file upload.
9875      */
9876
9877     /**
9878      * @cfg {Object} baseParams
9879      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9880      */
9881
9882     /**
9883      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9884      */
9885     timeout: 30,
9886     /**
9887      * @cfg {Sting} align (left|right) for navbar forms
9888      */
9889     align : 'left',
9890
9891     // private
9892     activeAction : null,
9893
9894     /**
9895      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9896      * element by passing it or its id or mask the form itself by passing in true.
9897      * @type Mixed
9898      */
9899     waitMsgTarget : false,
9900
9901     loadMask : true,
9902     
9903     /**
9904      * @cfg {Boolean} errorMask (true|false) default false
9905      */
9906     errorMask : false,
9907     
9908     /**
9909      * @cfg {Number} maskOffset Default 100
9910      */
9911     maskOffset : 100,
9912     
9913     /**
9914      * @cfg {Boolean} maskBody
9915      */
9916     maskBody : false,
9917
9918     getAutoCreate : function(){
9919
9920         var cfg = {
9921             tag: 'form',
9922             method : this.method || 'POST',
9923             id : this.id || Roo.id(),
9924             cls : ''
9925         };
9926         if (this.parent().xtype.match(/^Nav/)) {
9927             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9928
9929         }
9930
9931         if (this.labelAlign == 'left' ) {
9932             cfg.cls += ' form-horizontal';
9933         }
9934
9935
9936         return cfg;
9937     },
9938     initEvents : function()
9939     {
9940         this.el.on('submit', this.onSubmit, this);
9941         // this was added as random key presses on the form where triggering form submit.
9942         this.el.on('keypress', function(e) {
9943             if (e.getCharCode() != 13) {
9944                 return true;
9945             }
9946             // we might need to allow it for textareas.. and some other items.
9947             // check e.getTarget().
9948
9949             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9950                 return true;
9951             }
9952
9953             Roo.log("keypress blocked");
9954
9955             e.preventDefault();
9956             return false;
9957         });
9958         
9959     },
9960     // private
9961     onSubmit : function(e){
9962         e.stopEvent();
9963     },
9964
9965      /**
9966      * Returns true if client-side validation on the form is successful.
9967      * @return Boolean
9968      */
9969     isValid : function(){
9970         var items = this.getItems();
9971         var valid = true;
9972         var target = false;
9973         
9974         items.each(function(f){
9975             
9976             if(f.validate()){
9977                 return;
9978             }
9979             
9980             Roo.log('invalid field: ' + f.name);
9981             
9982             valid = false;
9983
9984             if(!target && f.el.isVisible(true)){
9985                 target = f;
9986             }
9987            
9988         });
9989         
9990         if(this.errorMask && !valid){
9991             Roo.bootstrap.Form.popover.mask(this, target);
9992         }
9993         
9994         return valid;
9995     },
9996     
9997     /**
9998      * Returns true if any fields in this form have changed since their original load.
9999      * @return Boolean
10000      */
10001     isDirty : function(){
10002         var dirty = false;
10003         var items = this.getItems();
10004         items.each(function(f){
10005            if(f.isDirty()){
10006                dirty = true;
10007                return false;
10008            }
10009            return true;
10010         });
10011         return dirty;
10012     },
10013      /**
10014      * Performs a predefined action (submit or load) or custom actions you define on this form.
10015      * @param {String} actionName The name of the action type
10016      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10017      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10018      * accept other config options):
10019      * <pre>
10020 Property          Type             Description
10021 ----------------  ---------------  ----------------------------------------------------------------------------------
10022 url               String           The url for the action (defaults to the form's url)
10023 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10024 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10025 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10026                                    validate the form on the client (defaults to false)
10027      * </pre>
10028      * @return {BasicForm} this
10029      */
10030     doAction : function(action, options){
10031         if(typeof action == 'string'){
10032             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10033         }
10034         if(this.fireEvent('beforeaction', this, action) !== false){
10035             this.beforeAction(action);
10036             action.run.defer(100, action);
10037         }
10038         return this;
10039     },
10040
10041     // private
10042     beforeAction : function(action){
10043         var o = action.options;
10044         
10045         if(this.loadMask){
10046             
10047             if(this.maskBody){
10048                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10049             } else {
10050                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10051             }
10052         }
10053         // not really supported yet.. ??
10054
10055         //if(this.waitMsgTarget === true){
10056         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10057         //}else if(this.waitMsgTarget){
10058         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10059         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10060         //}else {
10061         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10062        // }
10063
10064     },
10065
10066     // private
10067     afterAction : function(action, success){
10068         this.activeAction = null;
10069         var o = action.options;
10070
10071         if(this.loadMask){
10072             
10073             if(this.maskBody){
10074                 Roo.get(document.body).unmask();
10075             } else {
10076                 this.el.unmask();
10077             }
10078         }
10079         
10080         //if(this.waitMsgTarget === true){
10081 //            this.el.unmask();
10082         //}else if(this.waitMsgTarget){
10083         //    this.waitMsgTarget.unmask();
10084         //}else{
10085         //    Roo.MessageBox.updateProgress(1);
10086         //    Roo.MessageBox.hide();
10087        // }
10088         //
10089         if(success){
10090             if(o.reset){
10091                 this.reset();
10092             }
10093             Roo.callback(o.success, o.scope, [this, action]);
10094             this.fireEvent('actioncomplete', this, action);
10095
10096         }else{
10097
10098             // failure condition..
10099             // we have a scenario where updates need confirming.
10100             // eg. if a locking scenario exists..
10101             // we look for { errors : { needs_confirm : true }} in the response.
10102             if (
10103                 (typeof(action.result) != 'undefined')  &&
10104                 (typeof(action.result.errors) != 'undefined')  &&
10105                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10106            ){
10107                 var _t = this;
10108                 Roo.log("not supported yet");
10109                  /*
10110
10111                 Roo.MessageBox.confirm(
10112                     "Change requires confirmation",
10113                     action.result.errorMsg,
10114                     function(r) {
10115                         if (r != 'yes') {
10116                             return;
10117                         }
10118                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10119                     }
10120
10121                 );
10122                 */
10123
10124
10125                 return;
10126             }
10127
10128             Roo.callback(o.failure, o.scope, [this, action]);
10129             // show an error message if no failed handler is set..
10130             if (!this.hasListener('actionfailed')) {
10131                 Roo.log("need to add dialog support");
10132                 /*
10133                 Roo.MessageBox.alert("Error",
10134                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10135                         action.result.errorMsg :
10136                         "Saving Failed, please check your entries or try again"
10137                 );
10138                 */
10139             }
10140
10141             this.fireEvent('actionfailed', this, action);
10142         }
10143
10144     },
10145     /**
10146      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10147      * @param {String} id The value to search for
10148      * @return Field
10149      */
10150     findField : function(id){
10151         var items = this.getItems();
10152         var field = items.get(id);
10153         if(!field){
10154              items.each(function(f){
10155                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10156                     field = f;
10157                     return false;
10158                 }
10159                 return true;
10160             });
10161         }
10162         return field || null;
10163     },
10164      /**
10165      * Mark fields in this form invalid in bulk.
10166      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10167      * @return {BasicForm} this
10168      */
10169     markInvalid : function(errors){
10170         if(errors instanceof Array){
10171             for(var i = 0, len = errors.length; i < len; i++){
10172                 var fieldError = errors[i];
10173                 var f = this.findField(fieldError.id);
10174                 if(f){
10175                     f.markInvalid(fieldError.msg);
10176                 }
10177             }
10178         }else{
10179             var field, id;
10180             for(id in errors){
10181                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10182                     field.markInvalid(errors[id]);
10183                 }
10184             }
10185         }
10186         //Roo.each(this.childForms || [], function (f) {
10187         //    f.markInvalid(errors);
10188         //});
10189
10190         return this;
10191     },
10192
10193     /**
10194      * Set values for fields in this form in bulk.
10195      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10196      * @return {BasicForm} this
10197      */
10198     setValues : function(values){
10199         if(values instanceof Array){ // array of objects
10200             for(var i = 0, len = values.length; i < len; i++){
10201                 var v = values[i];
10202                 var f = this.findField(v.id);
10203                 if(f){
10204                     f.setValue(v.value);
10205                     if(this.trackResetOnLoad){
10206                         f.originalValue = f.getValue();
10207                     }
10208                 }
10209             }
10210         }else{ // object hash
10211             var field, id;
10212             for(id in values){
10213                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10214
10215                     if (field.setFromData &&
10216                         field.valueField &&
10217                         field.displayField &&
10218                         // combos' with local stores can
10219                         // be queried via setValue()
10220                         // to set their value..
10221                         (field.store && !field.store.isLocal)
10222                         ) {
10223                         // it's a combo
10224                         var sd = { };
10225                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10226                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10227                         field.setFromData(sd);
10228
10229                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10230                         
10231                         field.setFromData(values);
10232                         
10233                     } else {
10234                         field.setValue(values[id]);
10235                     }
10236
10237
10238                     if(this.trackResetOnLoad){
10239                         field.originalValue = field.getValue();
10240                     }
10241                 }
10242             }
10243         }
10244
10245         //Roo.each(this.childForms || [], function (f) {
10246         //    f.setValues(values);
10247         //});
10248
10249         return this;
10250     },
10251
10252     /**
10253      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10254      * they are returned as an array.
10255      * @param {Boolean} asString
10256      * @return {Object}
10257      */
10258     getValues : function(asString){
10259         //if (this.childForms) {
10260             // copy values from the child forms
10261         //    Roo.each(this.childForms, function (f) {
10262         //        this.setValues(f.getValues());
10263         //    }, this);
10264         //}
10265
10266
10267
10268         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10269         if(asString === true){
10270             return fs;
10271         }
10272         return Roo.urlDecode(fs);
10273     },
10274
10275     /**
10276      * Returns the fields in this form as an object with key/value pairs.
10277      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10278      * @return {Object}
10279      */
10280     getFieldValues : function(with_hidden)
10281     {
10282         var items = this.getItems();
10283         var ret = {};
10284         items.each(function(f){
10285             
10286             if (!f.getName()) {
10287                 return;
10288             }
10289             
10290             var v = f.getValue();
10291             
10292             if (f.inputType =='radio') {
10293                 if (typeof(ret[f.getName()]) == 'undefined') {
10294                     ret[f.getName()] = ''; // empty..
10295                 }
10296
10297                 if (!f.el.dom.checked) {
10298                     return;
10299
10300                 }
10301                 v = f.el.dom.value;
10302
10303             }
10304             
10305             if(f.xtype == 'MoneyField'){
10306                 ret[f.currencyName] = f.getCurrency();
10307             }
10308
10309             // not sure if this supported any more..
10310             if ((typeof(v) == 'object') && f.getRawValue) {
10311                 v = f.getRawValue() ; // dates..
10312             }
10313             // combo boxes where name != hiddenName...
10314             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10315                 ret[f.name] = f.getRawValue();
10316             }
10317             ret[f.getName()] = v;
10318         });
10319
10320         return ret;
10321     },
10322
10323     /**
10324      * Clears all invalid messages in this form.
10325      * @return {BasicForm} this
10326      */
10327     clearInvalid : function(){
10328         var items = this.getItems();
10329
10330         items.each(function(f){
10331            f.clearInvalid();
10332         });
10333
10334         return this;
10335     },
10336
10337     /**
10338      * Resets this form.
10339      * @return {BasicForm} this
10340      */
10341     reset : function(){
10342         var items = this.getItems();
10343         items.each(function(f){
10344             f.reset();
10345         });
10346
10347         Roo.each(this.childForms || [], function (f) {
10348             f.reset();
10349         });
10350
10351
10352         return this;
10353     },
10354     
10355     getItems : function()
10356     {
10357         var r=new Roo.util.MixedCollection(false, function(o){
10358             return o.id || (o.id = Roo.id());
10359         });
10360         var iter = function(el) {
10361             if (el.inputEl) {
10362                 r.add(el);
10363             }
10364             if (!el.items) {
10365                 return;
10366             }
10367             Roo.each(el.items,function(e) {
10368                 iter(e);
10369             });
10370         };
10371
10372         iter(this);
10373         return r;
10374     },
10375     
10376     hideFields : function(items)
10377     {
10378         Roo.each(items, function(i){
10379             
10380             var f = this.findField(i);
10381             
10382             if(!f){
10383                 return;
10384             }
10385             
10386             f.hide();
10387             
10388         }, this);
10389     },
10390     
10391     showFields : function(items)
10392     {
10393         Roo.each(items, function(i){
10394             
10395             var f = this.findField(i);
10396             
10397             if(!f){
10398                 return;
10399             }
10400             
10401             f.show();
10402             
10403         }, this);
10404     }
10405
10406 });
10407
10408 Roo.apply(Roo.bootstrap.Form, {
10409     
10410     popover : {
10411         
10412         padding : 5,
10413         
10414         isApplied : false,
10415         
10416         isMasked : false,
10417         
10418         form : false,
10419         
10420         target : false,
10421         
10422         toolTip : false,
10423         
10424         intervalID : false,
10425         
10426         maskEl : false,
10427         
10428         apply : function()
10429         {
10430             if(this.isApplied){
10431                 return;
10432             }
10433             
10434             this.maskEl = {
10435                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10436                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10437                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10438                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10439             };
10440             
10441             this.maskEl.top.enableDisplayMode("block");
10442             this.maskEl.left.enableDisplayMode("block");
10443             this.maskEl.bottom.enableDisplayMode("block");
10444             this.maskEl.right.enableDisplayMode("block");
10445             
10446             this.toolTip = new Roo.bootstrap.Tooltip({
10447                 cls : 'roo-form-error-popover',
10448                 alignment : {
10449                     'left' : ['r-l', [-2,0], 'right'],
10450                     'right' : ['l-r', [2,0], 'left'],
10451                     'bottom' : ['tl-bl', [0,2], 'top'],
10452                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10453                 }
10454             });
10455             
10456             this.toolTip.render(Roo.get(document.body));
10457
10458             this.toolTip.el.enableDisplayMode("block");
10459             
10460             Roo.get(document.body).on('click', function(){
10461                 this.unmask();
10462             }, this);
10463             
10464             Roo.get(document.body).on('touchstart', function(){
10465                 this.unmask();
10466             }, this);
10467             
10468             this.isApplied = true
10469         },
10470         
10471         mask : function(form, target)
10472         {
10473             this.form = form;
10474             
10475             this.target = target;
10476             
10477             if(!this.form.errorMask || !target.el){
10478                 return;
10479             }
10480             
10481             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10482             
10483             Roo.log(scrollable);
10484             
10485             var ot = this.target.el.calcOffsetsTo(scrollable);
10486             
10487             var scrollTo = ot[1] - this.form.maskOffset;
10488             
10489             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10490             
10491             scrollable.scrollTo('top', scrollTo);
10492             
10493             var box = this.target.el.getBox();
10494             Roo.log(box);
10495             var zIndex = Roo.bootstrap.Modal.zIndex++;
10496
10497             
10498             this.maskEl.top.setStyle('position', 'absolute');
10499             this.maskEl.top.setStyle('z-index', zIndex);
10500             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10501             this.maskEl.top.setLeft(0);
10502             this.maskEl.top.setTop(0);
10503             this.maskEl.top.show();
10504             
10505             this.maskEl.left.setStyle('position', 'absolute');
10506             this.maskEl.left.setStyle('z-index', zIndex);
10507             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10508             this.maskEl.left.setLeft(0);
10509             this.maskEl.left.setTop(box.y - this.padding);
10510             this.maskEl.left.show();
10511
10512             this.maskEl.bottom.setStyle('position', 'absolute');
10513             this.maskEl.bottom.setStyle('z-index', zIndex);
10514             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10515             this.maskEl.bottom.setLeft(0);
10516             this.maskEl.bottom.setTop(box.bottom + this.padding);
10517             this.maskEl.bottom.show();
10518
10519             this.maskEl.right.setStyle('position', 'absolute');
10520             this.maskEl.right.setStyle('z-index', zIndex);
10521             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10522             this.maskEl.right.setLeft(box.right + this.padding);
10523             this.maskEl.right.setTop(box.y - this.padding);
10524             this.maskEl.right.show();
10525
10526             this.toolTip.bindEl = this.target.el;
10527
10528             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10529
10530             var tip = this.target.blankText;
10531
10532             if(this.target.getValue() !== '' ) {
10533                 
10534                 if (this.target.invalidText.length) {
10535                     tip = this.target.invalidText;
10536                 } else if (this.target.regexText.length){
10537                     tip = this.target.regexText;
10538                 }
10539             }
10540
10541             this.toolTip.show(tip);
10542
10543             this.intervalID = window.setInterval(function() {
10544                 Roo.bootstrap.Form.popover.unmask();
10545             }, 10000);
10546
10547             window.onwheel = function(){ return false;};
10548             
10549             (function(){ this.isMasked = true; }).defer(500, this);
10550             
10551         },
10552         
10553         unmask : function()
10554         {
10555             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10556                 return;
10557             }
10558             
10559             this.maskEl.top.setStyle('position', 'absolute');
10560             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10561             this.maskEl.top.hide();
10562
10563             this.maskEl.left.setStyle('position', 'absolute');
10564             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10565             this.maskEl.left.hide();
10566
10567             this.maskEl.bottom.setStyle('position', 'absolute');
10568             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10569             this.maskEl.bottom.hide();
10570
10571             this.maskEl.right.setStyle('position', 'absolute');
10572             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10573             this.maskEl.right.hide();
10574             
10575             this.toolTip.hide();
10576             
10577             this.toolTip.el.hide();
10578             
10579             window.onwheel = function(){ return true;};
10580             
10581             if(this.intervalID){
10582                 window.clearInterval(this.intervalID);
10583                 this.intervalID = false;
10584             }
10585             
10586             this.isMasked = false;
10587             
10588         }
10589         
10590     }
10591     
10592 });
10593
10594 /*
10595  * Based on:
10596  * Ext JS Library 1.1.1
10597  * Copyright(c) 2006-2007, Ext JS, LLC.
10598  *
10599  * Originally Released Under LGPL - original licence link has changed is not relivant.
10600  *
10601  * Fork - LGPL
10602  * <script type="text/javascript">
10603  */
10604 /**
10605  * @class Roo.form.VTypes
10606  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10607  * @singleton
10608  */
10609 Roo.form.VTypes = function(){
10610     // closure these in so they are only created once.
10611     var alpha = /^[a-zA-Z_]+$/;
10612     var alphanum = /^[a-zA-Z0-9_]+$/;
10613     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10614     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10615
10616     // All these messages and functions are configurable
10617     return {
10618         /**
10619          * The function used to validate email addresses
10620          * @param {String} value The email address
10621          */
10622         'email' : function(v){
10623             return email.test(v);
10624         },
10625         /**
10626          * The error text to display when the email validation function returns false
10627          * @type String
10628          */
10629         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10630         /**
10631          * The keystroke filter mask to be applied on email input
10632          * @type RegExp
10633          */
10634         'emailMask' : /[a-z0-9_\.\-@]/i,
10635
10636         /**
10637          * The function used to validate URLs
10638          * @param {String} value The URL
10639          */
10640         'url' : function(v){
10641             return url.test(v);
10642         },
10643         /**
10644          * The error text to display when the url validation function returns false
10645          * @type String
10646          */
10647         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10648         
10649         /**
10650          * The function used to validate alpha values
10651          * @param {String} value The value
10652          */
10653         'alpha' : function(v){
10654             return alpha.test(v);
10655         },
10656         /**
10657          * The error text to display when the alpha validation function returns false
10658          * @type String
10659          */
10660         'alphaText' : 'This field should only contain letters and _',
10661         /**
10662          * The keystroke filter mask to be applied on alpha input
10663          * @type RegExp
10664          */
10665         'alphaMask' : /[a-z_]/i,
10666
10667         /**
10668          * The function used to validate alphanumeric values
10669          * @param {String} value The value
10670          */
10671         'alphanum' : function(v){
10672             return alphanum.test(v);
10673         },
10674         /**
10675          * The error text to display when the alphanumeric validation function returns false
10676          * @type String
10677          */
10678         'alphanumText' : 'This field should only contain letters, numbers and _',
10679         /**
10680          * The keystroke filter mask to be applied on alphanumeric input
10681          * @type RegExp
10682          */
10683         'alphanumMask' : /[a-z0-9_]/i
10684     };
10685 }();/*
10686  * - LGPL
10687  *
10688  * Input
10689  * 
10690  */
10691
10692 /**
10693  * @class Roo.bootstrap.Input
10694  * @extends Roo.bootstrap.Component
10695  * Bootstrap Input class
10696  * @cfg {Boolean} disabled is it disabled
10697  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10698  * @cfg {String} name name of the input
10699  * @cfg {string} fieldLabel - the label associated
10700  * @cfg {string} placeholder - placeholder to put in text.
10701  * @cfg {string}  before - input group add on before
10702  * @cfg {string} after - input group add on after
10703  * @cfg {string} size - (lg|sm) or leave empty..
10704  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10705  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10706  * @cfg {Number} md colspan out of 12 for computer-sized screens
10707  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10708  * @cfg {string} value default value of the input
10709  * @cfg {Number} labelWidth set the width of label 
10710  * @cfg {Number} labellg set the width of label (1-12)
10711  * @cfg {Number} labelmd set the width of label (1-12)
10712  * @cfg {Number} labelsm set the width of label (1-12)
10713  * @cfg {Number} labelxs set the width of label (1-12)
10714  * @cfg {String} labelAlign (top|left)
10715  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10716  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10717  * @cfg {String} indicatorpos (left|right) default left
10718  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10719  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10720  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10721
10722  * @cfg {String} align (left|center|right) Default left
10723  * @cfg {Boolean} forceFeedback (true|false) Default false
10724  * 
10725  * @constructor
10726  * Create a new Input
10727  * @param {Object} config The config object
10728  */
10729
10730 Roo.bootstrap.Input = function(config){
10731     
10732     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10733     
10734     this.addEvents({
10735         /**
10736          * @event focus
10737          * Fires when this field receives input focus.
10738          * @param {Roo.form.Field} this
10739          */
10740         focus : true,
10741         /**
10742          * @event blur
10743          * Fires when this field loses input focus.
10744          * @param {Roo.form.Field} this
10745          */
10746         blur : true,
10747         /**
10748          * @event specialkey
10749          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10750          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10751          * @param {Roo.form.Field} this
10752          * @param {Roo.EventObject} e The event object
10753          */
10754         specialkey : true,
10755         /**
10756          * @event change
10757          * Fires just before the field blurs if the field value has changed.
10758          * @param {Roo.form.Field} this
10759          * @param {Mixed} newValue The new value
10760          * @param {Mixed} oldValue The original value
10761          */
10762         change : true,
10763         /**
10764          * @event invalid
10765          * Fires after the field has been marked as invalid.
10766          * @param {Roo.form.Field} this
10767          * @param {String} msg The validation message
10768          */
10769         invalid : true,
10770         /**
10771          * @event valid
10772          * Fires after the field has been validated with no errors.
10773          * @param {Roo.form.Field} this
10774          */
10775         valid : true,
10776          /**
10777          * @event keyup
10778          * Fires after the key up
10779          * @param {Roo.form.Field} this
10780          * @param {Roo.EventObject}  e The event Object
10781          */
10782         keyup : true,
10783         /**
10784          * @event paste
10785          * Fires after the user pastes into input
10786          * @param {Roo.form.Field} this
10787          * @param {Roo.EventObject}  e The event Object
10788          */
10789         paste : true
10790     });
10791 };
10792
10793 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10794      /**
10795      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10796       automatic validation (defaults to "keyup").
10797      */
10798     validationEvent : "keyup",
10799      /**
10800      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10801      */
10802     validateOnBlur : true,
10803     /**
10804      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10805      */
10806     validationDelay : 250,
10807      /**
10808      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10809      */
10810     focusClass : "x-form-focus",  // not needed???
10811     
10812        
10813     /**
10814      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10815      */
10816     invalidClass : "has-warning",
10817     
10818     /**
10819      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10820      */
10821     validClass : "has-success",
10822     
10823     /**
10824      * @cfg {Boolean} hasFeedback (true|false) default true
10825      */
10826     hasFeedback : true,
10827     
10828     /**
10829      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10830      */
10831     invalidFeedbackClass : "glyphicon-warning-sign",
10832     
10833     /**
10834      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10835      */
10836     validFeedbackClass : "glyphicon-ok",
10837     
10838     /**
10839      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10840      */
10841     selectOnFocus : false,
10842     
10843      /**
10844      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10845      */
10846     maskRe : null,
10847        /**
10848      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10849      */
10850     vtype : null,
10851     
10852       /**
10853      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10854      */
10855     disableKeyFilter : false,
10856     
10857        /**
10858      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10859      */
10860     disabled : false,
10861      /**
10862      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10863      */
10864     allowBlank : true,
10865     /**
10866      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10867      */
10868     blankText : "Please complete this mandatory field",
10869     
10870      /**
10871      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10872      */
10873     minLength : 0,
10874     /**
10875      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10876      */
10877     maxLength : Number.MAX_VALUE,
10878     /**
10879      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10880      */
10881     minLengthText : "The minimum length for this field is {0}",
10882     /**
10883      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10884      */
10885     maxLengthText : "The maximum length for this field is {0}",
10886   
10887     
10888     /**
10889      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10890      * If available, this function will be called only after the basic validators all return true, and will be passed the
10891      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10892      */
10893     validator : null,
10894     /**
10895      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10896      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10897      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10898      */
10899     regex : null,
10900     /**
10901      * @cfg {String} regexText -- Depricated - use Invalid Text
10902      */
10903     regexText : "",
10904     
10905     /**
10906      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10907      */
10908     invalidText : "",
10909     
10910     
10911     
10912     autocomplete: false,
10913     
10914     
10915     fieldLabel : '',
10916     inputType : 'text',
10917     
10918     name : false,
10919     placeholder: false,
10920     before : false,
10921     after : false,
10922     size : false,
10923     hasFocus : false,
10924     preventMark: false,
10925     isFormField : true,
10926     value : '',
10927     labelWidth : 2,
10928     labelAlign : false,
10929     readOnly : false,
10930     align : false,
10931     formatedValue : false,
10932     forceFeedback : false,
10933     
10934     indicatorpos : 'left',
10935     
10936     labellg : 0,
10937     labelmd : 0,
10938     labelsm : 0,
10939     labelxs : 0,
10940     
10941     capture : '',
10942     accept : '',
10943     
10944     parentLabelAlign : function()
10945     {
10946         var parent = this;
10947         while (parent.parent()) {
10948             parent = parent.parent();
10949             if (typeof(parent.labelAlign) !='undefined') {
10950                 return parent.labelAlign;
10951             }
10952         }
10953         return 'left';
10954         
10955     },
10956     
10957     getAutoCreate : function()
10958     {
10959         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10960         
10961         var id = Roo.id();
10962         
10963         var cfg = {};
10964         
10965         if(this.inputType != 'hidden'){
10966             cfg.cls = 'form-group' //input-group
10967         }
10968         
10969         var input =  {
10970             tag: 'input',
10971             id : id,
10972             type : this.inputType,
10973             value : this.value,
10974             cls : 'form-control',
10975             placeholder : this.placeholder || '',
10976             autocomplete : this.autocomplete || 'new-password'
10977         };
10978         if (this.inputType == 'file') {
10979             input.style = 'overflow:hidden'; // why not in CSS?
10980         }
10981         
10982         if(this.capture.length){
10983             input.capture = this.capture;
10984         }
10985         
10986         if(this.accept.length){
10987             input.accept = this.accept + "/*";
10988         }
10989         
10990         if(this.align){
10991             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10992         }
10993         
10994         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10995             input.maxLength = this.maxLength;
10996         }
10997         
10998         if (this.disabled) {
10999             input.disabled=true;
11000         }
11001         
11002         if (this.readOnly) {
11003             input.readonly=true;
11004         }
11005         
11006         if (this.name) {
11007             input.name = this.name;
11008         }
11009         
11010         if (this.size) {
11011             input.cls += ' input-' + this.size;
11012         }
11013         
11014         var settings=this;
11015         ['xs','sm','md','lg'].map(function(size){
11016             if (settings[size]) {
11017                 cfg.cls += ' col-' + size + '-' + settings[size];
11018             }
11019         });
11020         
11021         var inputblock = input;
11022         
11023         var feedback = {
11024             tag: 'span',
11025             cls: 'glyphicon form-control-feedback'
11026         };
11027             
11028         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11029             
11030             inputblock = {
11031                 cls : 'has-feedback',
11032                 cn :  [
11033                     input,
11034                     feedback
11035                 ] 
11036             };  
11037         }
11038         
11039         if (this.before || this.after) {
11040             
11041             inputblock = {
11042                 cls : 'input-group',
11043                 cn :  [] 
11044             };
11045             
11046             if (this.before && typeof(this.before) == 'string') {
11047                 
11048                 inputblock.cn.push({
11049                     tag :'span',
11050                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11051                     html : this.before
11052                 });
11053             }
11054             if (this.before && typeof(this.before) == 'object') {
11055                 this.before = Roo.factory(this.before);
11056                 
11057                 inputblock.cn.push({
11058                     tag :'span',
11059                     cls : 'roo-input-before input-group-prepend   input-group-' +
11060                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11061                 });
11062             }
11063             
11064             inputblock.cn.push(input);
11065             
11066             if (this.after && typeof(this.after) == 'string') {
11067                 inputblock.cn.push({
11068                     tag :'span',
11069                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11070                     html : this.after
11071                 });
11072             }
11073             if (this.after && typeof(this.after) == 'object') {
11074                 this.after = Roo.factory(this.after);
11075                 
11076                 inputblock.cn.push({
11077                     tag :'span',
11078                     cls : 'roo-input-after input-group-append  input-group-' +
11079                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11080                 });
11081             }
11082             
11083             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11084                 inputblock.cls += ' has-feedback';
11085                 inputblock.cn.push(feedback);
11086             }
11087         };
11088         var indicator = {
11089             tag : 'i',
11090             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11091             tooltip : 'This field is required'
11092         };
11093         if (this.allowBlank ) {
11094             indicator.style = this.allowBlank ? ' display:none' : '';
11095         }
11096         if (align ==='left' && this.fieldLabel.length) {
11097             
11098             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11099             
11100             cfg.cn = [
11101                 indicator,
11102                 {
11103                     tag: 'label',
11104                     'for' :  id,
11105                     cls : 'control-label col-form-label',
11106                     html : this.fieldLabel
11107
11108                 },
11109                 {
11110                     cls : "", 
11111                     cn: [
11112                         inputblock
11113                     ]
11114                 }
11115             ];
11116             
11117             var labelCfg = cfg.cn[1];
11118             var contentCfg = cfg.cn[2];
11119             
11120             if(this.indicatorpos == 'right'){
11121                 cfg.cn = [
11122                     {
11123                         tag: 'label',
11124                         'for' :  id,
11125                         cls : 'control-label col-form-label',
11126                         cn : [
11127                             {
11128                                 tag : 'span',
11129                                 html : this.fieldLabel
11130                             },
11131                             indicator
11132                         ]
11133                     },
11134                     {
11135                         cls : "",
11136                         cn: [
11137                             inputblock
11138                         ]
11139                     }
11140
11141                 ];
11142                 
11143                 labelCfg = cfg.cn[0];
11144                 contentCfg = cfg.cn[1];
11145             
11146             }
11147             
11148             if(this.labelWidth > 12){
11149                 labelCfg.style = "width: " + this.labelWidth + 'px';
11150             }
11151             
11152             if(this.labelWidth < 13 && this.labelmd == 0){
11153                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11154             }
11155             
11156             if(this.labellg > 0){
11157                 labelCfg.cls += ' col-lg-' + this.labellg;
11158                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11159             }
11160             
11161             if(this.labelmd > 0){
11162                 labelCfg.cls += ' col-md-' + this.labelmd;
11163                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11164             }
11165             
11166             if(this.labelsm > 0){
11167                 labelCfg.cls += ' col-sm-' + this.labelsm;
11168                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11169             }
11170             
11171             if(this.labelxs > 0){
11172                 labelCfg.cls += ' col-xs-' + this.labelxs;
11173                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11174             }
11175             
11176             
11177         } else if ( this.fieldLabel.length) {
11178                 
11179             
11180             
11181             cfg.cn = [
11182                 {
11183                     tag : 'i',
11184                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11185                     tooltip : 'This field is required',
11186                     style : this.allowBlank ? ' display:none' : '' 
11187                 },
11188                 {
11189                     tag: 'label',
11190                    //cls : 'input-group-addon',
11191                     html : this.fieldLabel
11192
11193                 },
11194
11195                inputblock
11196
11197            ];
11198            
11199            if(this.indicatorpos == 'right'){
11200        
11201                 cfg.cn = [
11202                     {
11203                         tag: 'label',
11204                        //cls : 'input-group-addon',
11205                         html : this.fieldLabel
11206
11207                     },
11208                     {
11209                         tag : 'i',
11210                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11211                         tooltip : 'This field is required',
11212                         style : this.allowBlank ? ' display:none' : '' 
11213                     },
11214
11215                    inputblock
11216
11217                ];
11218
11219             }
11220
11221         } else {
11222             
11223             cfg.cn = [
11224
11225                     inputblock
11226
11227             ];
11228                 
11229                 
11230         };
11231         
11232         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11233            cfg.cls += ' navbar-form';
11234         }
11235         
11236         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11237             // on BS4 we do this only if not form 
11238             cfg.cls += ' navbar-form';
11239             cfg.tag = 'li';
11240         }
11241         
11242         return cfg;
11243         
11244     },
11245     /**
11246      * return the real input element.
11247      */
11248     inputEl: function ()
11249     {
11250         return this.el.select('input.form-control',true).first();
11251     },
11252     
11253     tooltipEl : function()
11254     {
11255         return this.inputEl();
11256     },
11257     
11258     indicatorEl : function()
11259     {
11260         if (Roo.bootstrap.version == 4) {
11261             return false; // not enabled in v4 yet.
11262         }
11263         
11264         var indicator = this.el.select('i.roo-required-indicator',true).first();
11265         
11266         if(!indicator){
11267             return false;
11268         }
11269         
11270         return indicator;
11271         
11272     },
11273     
11274     setDisabled : function(v)
11275     {
11276         var i  = this.inputEl().dom;
11277         if (!v) {
11278             i.removeAttribute('disabled');
11279             return;
11280             
11281         }
11282         i.setAttribute('disabled','true');
11283     },
11284     initEvents : function()
11285     {
11286           
11287         this.inputEl().on("keydown" , this.fireKey,  this);
11288         this.inputEl().on("focus", this.onFocus,  this);
11289         this.inputEl().on("blur", this.onBlur,  this);
11290         
11291         this.inputEl().relayEvent('keyup', this);
11292         this.inputEl().relayEvent('paste', this);
11293         
11294         this.indicator = this.indicatorEl();
11295         
11296         if(this.indicator){
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11298         }
11299  
11300         // reference to original value for reset
11301         this.originalValue = this.getValue();
11302         //Roo.form.TextField.superclass.initEvents.call(this);
11303         if(this.validationEvent == 'keyup'){
11304             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11305             this.inputEl().on('keyup', this.filterValidation, this);
11306         }
11307         else if(this.validationEvent !== false){
11308             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11309         }
11310         
11311         if(this.selectOnFocus){
11312             this.on("focus", this.preFocus, this);
11313             
11314         }
11315         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11316             this.inputEl().on("keypress", this.filterKeys, this);
11317         } else {
11318             this.inputEl().relayEvent('keypress', this);
11319         }
11320        /* if(this.grow){
11321             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11322             this.el.on("click", this.autoSize,  this);
11323         }
11324         */
11325         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11326             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11327         }
11328         
11329         if (typeof(this.before) == 'object') {
11330             this.before.render(this.el.select('.roo-input-before',true).first());
11331         }
11332         if (typeof(this.after) == 'object') {
11333             this.after.render(this.el.select('.roo-input-after',true).first());
11334         }
11335         
11336         this.inputEl().on('change', this.onChange, this);
11337         
11338     },
11339     filterValidation : function(e){
11340         if(!e.isNavKeyPress()){
11341             this.validationTask.delay(this.validationDelay);
11342         }
11343     },
11344      /**
11345      * Validates the field value
11346      * @return {Boolean} True if the value is valid, else false
11347      */
11348     validate : function(){
11349         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11350         if(this.disabled || this.validateValue(this.getRawValue())){
11351             this.markValid();
11352             return true;
11353         }
11354         
11355         this.markInvalid();
11356         return false;
11357     },
11358     
11359     
11360     /**
11361      * Validates a value according to the field's validation rules and marks the field as invalid
11362      * if the validation fails
11363      * @param {Mixed} value The value to validate
11364      * @return {Boolean} True if the value is valid, else false
11365      */
11366     validateValue : function(value)
11367     {
11368         if(this.getVisibilityEl().hasClass('hidden')){
11369             return true;
11370         }
11371         
11372         if(value.length < 1)  { // if it's blank
11373             if(this.allowBlank){
11374                 return true;
11375             }
11376             return false;
11377         }
11378         
11379         if(value.length < this.minLength){
11380             return false;
11381         }
11382         if(value.length > this.maxLength){
11383             return false;
11384         }
11385         if(this.vtype){
11386             var vt = Roo.form.VTypes;
11387             if(!vt[this.vtype](value, this)){
11388                 return false;
11389             }
11390         }
11391         if(typeof this.validator == "function"){
11392             var msg = this.validator(value);
11393             if(msg !== true){
11394                 return false;
11395             }
11396             if (typeof(msg) == 'string') {
11397                 this.invalidText = msg;
11398             }
11399         }
11400         
11401         if(this.regex && !this.regex.test(value)){
11402             return false;
11403         }
11404         
11405         return true;
11406     },
11407     
11408      // private
11409     fireKey : function(e){
11410         //Roo.log('field ' + e.getKey());
11411         if(e.isNavKeyPress()){
11412             this.fireEvent("specialkey", this, e);
11413         }
11414     },
11415     focus : function (selectText){
11416         if(this.rendered){
11417             this.inputEl().focus();
11418             if(selectText === true){
11419                 this.inputEl().dom.select();
11420             }
11421         }
11422         return this;
11423     } ,
11424     
11425     onFocus : function(){
11426         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11427            // this.el.addClass(this.focusClass);
11428         }
11429         if(!this.hasFocus){
11430             this.hasFocus = true;
11431             this.startValue = this.getValue();
11432             this.fireEvent("focus", this);
11433         }
11434     },
11435     
11436     beforeBlur : Roo.emptyFn,
11437
11438     
11439     // private
11440     onBlur : function(){
11441         this.beforeBlur();
11442         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11443             //this.el.removeClass(this.focusClass);
11444         }
11445         this.hasFocus = false;
11446         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11447             this.validate();
11448         }
11449         var v = this.getValue();
11450         if(String(v) !== String(this.startValue)){
11451             this.fireEvent('change', this, v, this.startValue);
11452         }
11453         this.fireEvent("blur", this);
11454     },
11455     
11456     onChange : function(e)
11457     {
11458         var v = this.getValue();
11459         if(String(v) !== String(this.startValue)){
11460             this.fireEvent('change', this, v, this.startValue);
11461         }
11462         
11463     },
11464     
11465     /**
11466      * Resets the current field value to the originally loaded value and clears any validation messages
11467      */
11468     reset : function(){
11469         this.setValue(this.originalValue);
11470         this.validate();
11471     },
11472      /**
11473      * Returns the name of the field
11474      * @return {Mixed} name The name field
11475      */
11476     getName: function(){
11477         return this.name;
11478     },
11479      /**
11480      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11481      * @return {Mixed} value The field value
11482      */
11483     getValue : function(){
11484         
11485         var v = this.inputEl().getValue();
11486         
11487         return v;
11488     },
11489     /**
11490      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11491      * @return {Mixed} value The field value
11492      */
11493     getRawValue : function(){
11494         var v = this.inputEl().getValue();
11495         
11496         return v;
11497     },
11498     
11499     /**
11500      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11501      * @param {Mixed} value The value to set
11502      */
11503     setRawValue : function(v){
11504         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11505     },
11506     
11507     selectText : function(start, end){
11508         var v = this.getRawValue();
11509         if(v.length > 0){
11510             start = start === undefined ? 0 : start;
11511             end = end === undefined ? v.length : end;
11512             var d = this.inputEl().dom;
11513             if(d.setSelectionRange){
11514                 d.setSelectionRange(start, end);
11515             }else if(d.createTextRange){
11516                 var range = d.createTextRange();
11517                 range.moveStart("character", start);
11518                 range.moveEnd("character", v.length-end);
11519                 range.select();
11520             }
11521         }
11522     },
11523     
11524     /**
11525      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11526      * @param {Mixed} value The value to set
11527      */
11528     setValue : function(v){
11529         this.value = v;
11530         if(this.rendered){
11531             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11532             this.validate();
11533         }
11534     },
11535     
11536     /*
11537     processValue : function(value){
11538         if(this.stripCharsRe){
11539             var newValue = value.replace(this.stripCharsRe, '');
11540             if(newValue !== value){
11541                 this.setRawValue(newValue);
11542                 return newValue;
11543             }
11544         }
11545         return value;
11546     },
11547   */
11548     preFocus : function(){
11549         
11550         if(this.selectOnFocus){
11551             this.inputEl().dom.select();
11552         }
11553     },
11554     filterKeys : function(e){
11555         var k = e.getKey();
11556         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11557             return;
11558         }
11559         var c = e.getCharCode(), cc = String.fromCharCode(c);
11560         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11561             return;
11562         }
11563         if(!this.maskRe.test(cc)){
11564             e.stopEvent();
11565         }
11566     },
11567      /**
11568      * Clear any invalid styles/messages for this field
11569      */
11570     clearInvalid : function(){
11571         
11572         if(!this.el || this.preventMark){ // not rendered
11573             return;
11574         }
11575         
11576         
11577         this.el.removeClass([this.invalidClass, 'is-invalid']);
11578         
11579         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11580             
11581             var feedback = this.el.select('.form-control-feedback', true).first();
11582             
11583             if(feedback){
11584                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11585             }
11586             
11587         }
11588         
11589         if(this.indicator){
11590             this.indicator.removeClass('visible');
11591             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11592         }
11593         
11594         this.fireEvent('valid', this);
11595     },
11596     
11597      /**
11598      * Mark this field as valid
11599      */
11600     markValid : function()
11601     {
11602         if(!this.el  || this.preventMark){ // not rendered...
11603             return;
11604         }
11605         
11606         this.el.removeClass([this.invalidClass, this.validClass]);
11607         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11608
11609         var feedback = this.el.select('.form-control-feedback', true).first();
11610             
11611         if(feedback){
11612             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11613         }
11614         
11615         if(this.indicator){
11616             this.indicator.removeClass('visible');
11617             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11618         }
11619         
11620         if(this.disabled){
11621             return;
11622         }
11623         
11624            
11625         if(this.allowBlank && !this.getRawValue().length){
11626             return;
11627         }
11628         if (Roo.bootstrap.version == 3) {
11629             this.el.addClass(this.validClass);
11630         } else {
11631             this.inputEl().addClass('is-valid');
11632         }
11633
11634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11635             
11636             var feedback = this.el.select('.form-control-feedback', true).first();
11637             
11638             if(feedback){
11639                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11640                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11641             }
11642             
11643         }
11644         
11645         this.fireEvent('valid', this);
11646     },
11647     
11648      /**
11649      * Mark this field as invalid
11650      * @param {String} msg The validation message
11651      */
11652     markInvalid : function(msg)
11653     {
11654         if(!this.el  || this.preventMark){ // not rendered
11655             return;
11656         }
11657         
11658         this.el.removeClass([this.invalidClass, this.validClass]);
11659         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11660         
11661         var feedback = this.el.select('.form-control-feedback', true).first();
11662             
11663         if(feedback){
11664             this.el.select('.form-control-feedback', true).first().removeClass(
11665                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11666         }
11667
11668         if(this.disabled){
11669             return;
11670         }
11671         
11672         if(this.allowBlank && !this.getRawValue().length){
11673             return;
11674         }
11675         
11676         if(this.indicator){
11677             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11678             this.indicator.addClass('visible');
11679         }
11680         if (Roo.bootstrap.version == 3) {
11681             this.el.addClass(this.invalidClass);
11682         } else {
11683             this.inputEl().addClass('is-invalid');
11684         }
11685         
11686         
11687         
11688         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11689             
11690             var feedback = this.el.select('.form-control-feedback', true).first();
11691             
11692             if(feedback){
11693                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11694                 
11695                 if(this.getValue().length || this.forceFeedback){
11696                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11697                 }
11698                 
11699             }
11700             
11701         }
11702         
11703         this.fireEvent('invalid', this, msg);
11704     },
11705     // private
11706     SafariOnKeyDown : function(event)
11707     {
11708         // this is a workaround for a password hang bug on chrome/ webkit.
11709         if (this.inputEl().dom.type != 'password') {
11710             return;
11711         }
11712         
11713         var isSelectAll = false;
11714         
11715         if(this.inputEl().dom.selectionEnd > 0){
11716             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11717         }
11718         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11719             event.preventDefault();
11720             this.setValue('');
11721             return;
11722         }
11723         
11724         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11725             
11726             event.preventDefault();
11727             // this is very hacky as keydown always get's upper case.
11728             //
11729             var cc = String.fromCharCode(event.getCharCode());
11730             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11731             
11732         }
11733     },
11734     adjustWidth : function(tag, w){
11735         tag = tag.toLowerCase();
11736         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11737             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11738                 if(tag == 'input'){
11739                     return w + 2;
11740                 }
11741                 if(tag == 'textarea'){
11742                     return w-2;
11743                 }
11744             }else if(Roo.isOpera){
11745                 if(tag == 'input'){
11746                     return w + 2;
11747                 }
11748                 if(tag == 'textarea'){
11749                     return w-2;
11750                 }
11751             }
11752         }
11753         return w;
11754     },
11755     
11756     setFieldLabel : function(v)
11757     {
11758         if(!this.rendered){
11759             return;
11760         }
11761         
11762         if(this.indicatorEl()){
11763             var ar = this.el.select('label > span',true);
11764             
11765             if (ar.elements.length) {
11766                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11767                 this.fieldLabel = v;
11768                 return;
11769             }
11770             
11771             var br = this.el.select('label',true);
11772             
11773             if(br.elements.length) {
11774                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11775                 this.fieldLabel = v;
11776                 return;
11777             }
11778             
11779             Roo.log('Cannot Found any of label > span || label in input');
11780             return;
11781         }
11782         
11783         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784         this.fieldLabel = v;
11785         
11786         
11787     }
11788 });
11789
11790  
11791 /*
11792  * - LGPL
11793  *
11794  * Input
11795  * 
11796  */
11797
11798 /**
11799  * @class Roo.bootstrap.TextArea
11800  * @extends Roo.bootstrap.Input
11801  * Bootstrap TextArea class
11802  * @cfg {Number} cols Specifies the visible width of a text area
11803  * @cfg {Number} rows Specifies the visible number of lines in a text area
11804  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11805  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11806  * @cfg {string} html text
11807  * 
11808  * @constructor
11809  * Create a new TextArea
11810  * @param {Object} config The config object
11811  */
11812
11813 Roo.bootstrap.TextArea = function(config){
11814     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11815    
11816 };
11817
11818 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11819      
11820     cols : false,
11821     rows : 5,
11822     readOnly : false,
11823     warp : 'soft',
11824     resize : false,
11825     value: false,
11826     html: false,
11827     
11828     getAutoCreate : function(){
11829         
11830         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11831         
11832         var id = Roo.id();
11833         
11834         var cfg = {};
11835         
11836         if(this.inputType != 'hidden'){
11837             cfg.cls = 'form-group' //input-group
11838         }
11839         
11840         var input =  {
11841             tag: 'textarea',
11842             id : id,
11843             warp : this.warp,
11844             rows : this.rows,
11845             value : this.value || '',
11846             html: this.html || '',
11847             cls : 'form-control',
11848             placeholder : this.placeholder || '' 
11849             
11850         };
11851         
11852         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11853             input.maxLength = this.maxLength;
11854         }
11855         
11856         if(this.resize){
11857             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11858         }
11859         
11860         if(this.cols){
11861             input.cols = this.cols;
11862         }
11863         
11864         if (this.readOnly) {
11865             input.readonly = true;
11866         }
11867         
11868         if (this.name) {
11869             input.name = this.name;
11870         }
11871         
11872         if (this.size) {
11873             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11874         }
11875         
11876         var settings=this;
11877         ['xs','sm','md','lg'].map(function(size){
11878             if (settings[size]) {
11879                 cfg.cls += ' col-' + size + '-' + settings[size];
11880             }
11881         });
11882         
11883         var inputblock = input;
11884         
11885         if(this.hasFeedback && !this.allowBlank){
11886             
11887             var feedback = {
11888                 tag: 'span',
11889                 cls: 'glyphicon form-control-feedback'
11890             };
11891
11892             inputblock = {
11893                 cls : 'has-feedback',
11894                 cn :  [
11895                     input,
11896                     feedback
11897                 ] 
11898             };  
11899         }
11900         
11901         
11902         if (this.before || this.after) {
11903             
11904             inputblock = {
11905                 cls : 'input-group',
11906                 cn :  [] 
11907             };
11908             if (this.before) {
11909                 inputblock.cn.push({
11910                     tag :'span',
11911                     cls : 'input-group-addon',
11912                     html : this.before
11913                 });
11914             }
11915             
11916             inputblock.cn.push(input);
11917             
11918             if(this.hasFeedback && !this.allowBlank){
11919                 inputblock.cls += ' has-feedback';
11920                 inputblock.cn.push(feedback);
11921             }
11922             
11923             if (this.after) {
11924                 inputblock.cn.push({
11925                     tag :'span',
11926                     cls : 'input-group-addon',
11927                     html : this.after
11928                 });
11929             }
11930             
11931         }
11932         
11933         if (align ==='left' && this.fieldLabel.length) {
11934             cfg.cn = [
11935                 {
11936                     tag: 'label',
11937                     'for' :  id,
11938                     cls : 'control-label',
11939                     html : this.fieldLabel
11940                 },
11941                 {
11942                     cls : "",
11943                     cn: [
11944                         inputblock
11945                     ]
11946                 }
11947
11948             ];
11949             
11950             if(this.labelWidth > 12){
11951                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11952             }
11953
11954             if(this.labelWidth < 13 && this.labelmd == 0){
11955                 this.labelmd = this.labelWidth;
11956             }
11957
11958             if(this.labellg > 0){
11959                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11960                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11961             }
11962
11963             if(this.labelmd > 0){
11964                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11965                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11966             }
11967
11968             if(this.labelsm > 0){
11969                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11970                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11971             }
11972
11973             if(this.labelxs > 0){
11974                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11975                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11976             }
11977             
11978         } else if ( this.fieldLabel.length) {
11979             cfg.cn = [
11980
11981                {
11982                    tag: 'label',
11983                    //cls : 'input-group-addon',
11984                    html : this.fieldLabel
11985
11986                },
11987
11988                inputblock
11989
11990            ];
11991
11992         } else {
11993
11994             cfg.cn = [
11995
11996                 inputblock
11997
11998             ];
11999                 
12000         }
12001         
12002         if (this.disabled) {
12003             input.disabled=true;
12004         }
12005         
12006         return cfg;
12007         
12008     },
12009     /**
12010      * return the real textarea element.
12011      */
12012     inputEl: function ()
12013     {
12014         return this.el.select('textarea.form-control',true).first();
12015     },
12016     
12017     /**
12018      * Clear any invalid styles/messages for this field
12019      */
12020     clearInvalid : function()
12021     {
12022         
12023         if(!this.el || this.preventMark){ // not rendered
12024             return;
12025         }
12026         
12027         var label = this.el.select('label', true).first();
12028         var icon = this.el.select('i.fa-star', true).first();
12029         
12030         if(label && icon){
12031             icon.remove();
12032         }
12033         this.el.removeClass( this.validClass);
12034         this.inputEl().removeClass('is-invalid');
12035          
12036         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12037             
12038             var feedback = this.el.select('.form-control-feedback', true).first();
12039             
12040             if(feedback){
12041                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12042             }
12043             
12044         }
12045         
12046         this.fireEvent('valid', this);
12047     },
12048     
12049      /**
12050      * Mark this field as valid
12051      */
12052     markValid : function()
12053     {
12054         if(!this.el  || this.preventMark){ // not rendered
12055             return;
12056         }
12057         
12058         this.el.removeClass([this.invalidClass, this.validClass]);
12059         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12060         
12061         var feedback = this.el.select('.form-control-feedback', true).first();
12062             
12063         if(feedback){
12064             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12065         }
12066
12067         if(this.disabled || this.allowBlank){
12068             return;
12069         }
12070         
12071         var label = this.el.select('label', true).first();
12072         var icon = this.el.select('i.fa-star', true).first();
12073         
12074         if(label && icon){
12075             icon.remove();
12076         }
12077         if (Roo.bootstrap.version == 3) {
12078             this.el.addClass(this.validClass);
12079         } else {
12080             this.inputEl().addClass('is-valid');
12081         }
12082         
12083         
12084         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12085             
12086             var feedback = this.el.select('.form-control-feedback', true).first();
12087             
12088             if(feedback){
12089                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12090                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12091             }
12092             
12093         }
12094         
12095         this.fireEvent('valid', this);
12096     },
12097     
12098      /**
12099      * Mark this field as invalid
12100      * @param {String} msg The validation message
12101      */
12102     markInvalid : function(msg)
12103     {
12104         if(!this.el  || this.preventMark){ // not rendered
12105             return;
12106         }
12107         
12108         this.el.removeClass([this.invalidClass, this.validClass]);
12109         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12110         
12111         var feedback = this.el.select('.form-control-feedback', true).first();
12112             
12113         if(feedback){
12114             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12115         }
12116
12117         if(this.disabled || this.allowBlank){
12118             return;
12119         }
12120         
12121         var label = this.el.select('label', true).first();
12122         var icon = this.el.select('i.fa-star', true).first();
12123         
12124         if(!this.getValue().length && label && !icon){
12125             this.el.createChild({
12126                 tag : 'i',
12127                 cls : 'text-danger fa fa-lg fa-star',
12128                 tooltip : 'This field is required',
12129                 style : 'margin-right:5px;'
12130             }, label, true);
12131         }
12132         
12133         if (Roo.bootstrap.version == 3) {
12134             this.el.addClass(this.invalidClass);
12135         } else {
12136             this.inputEl().addClass('is-invalid');
12137         }
12138         
12139         // fixme ... this may be depricated need to test..
12140         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12141             
12142             var feedback = this.el.select('.form-control-feedback', true).first();
12143             
12144             if(feedback){
12145                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12146                 
12147                 if(this.getValue().length || this.forceFeedback){
12148                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12149                 }
12150                 
12151             }
12152             
12153         }
12154         
12155         this.fireEvent('invalid', this, msg);
12156     }
12157 });
12158
12159  
12160 /*
12161  * - LGPL
12162  *
12163  * trigger field - base class for combo..
12164  * 
12165  */
12166  
12167 /**
12168  * @class Roo.bootstrap.TriggerField
12169  * @extends Roo.bootstrap.Input
12170  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12171  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12172  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12173  * for which you can provide a custom implementation.  For example:
12174  * <pre><code>
12175 var trigger = new Roo.bootstrap.TriggerField();
12176 trigger.onTriggerClick = myTriggerFn;
12177 trigger.applyTo('my-field');
12178 </code></pre>
12179  *
12180  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12181  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12182  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12183  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12184  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12185
12186  * @constructor
12187  * Create a new TriggerField.
12188  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12189  * to the base TextField)
12190  */
12191 Roo.bootstrap.TriggerField = function(config){
12192     this.mimicing = false;
12193     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12194 };
12195
12196 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12197     /**
12198      * @cfg {String} triggerClass A CSS class to apply to the trigger
12199      */
12200      /**
12201      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12202      */
12203     hideTrigger:false,
12204
12205     /**
12206      * @cfg {Boolean} removable (true|false) special filter default false
12207      */
12208     removable : false,
12209     
12210     /** @cfg {Boolean} grow @hide */
12211     /** @cfg {Number} growMin @hide */
12212     /** @cfg {Number} growMax @hide */
12213
12214     /**
12215      * @hide 
12216      * @method
12217      */
12218     autoSize: Roo.emptyFn,
12219     // private
12220     monitorTab : true,
12221     // private
12222     deferHeight : true,
12223
12224     
12225     actionMode : 'wrap',
12226     
12227     caret : false,
12228     
12229     
12230     getAutoCreate : function(){
12231        
12232         var align = this.labelAlign || this.parentLabelAlign();
12233         
12234         var id = Roo.id();
12235         
12236         var cfg = {
12237             cls: 'form-group' //input-group
12238         };
12239         
12240         
12241         var input =  {
12242             tag: 'input',
12243             id : id,
12244             type : this.inputType,
12245             cls : 'form-control',
12246             autocomplete: 'new-password',
12247             placeholder : this.placeholder || '' 
12248             
12249         };
12250         if (this.name) {
12251             input.name = this.name;
12252         }
12253         if (this.size) {
12254             input.cls += ' input-' + this.size;
12255         }
12256         
12257         if (this.disabled) {
12258             input.disabled=true;
12259         }
12260         
12261         var inputblock = input;
12262         
12263         if(this.hasFeedback && !this.allowBlank){
12264             
12265             var feedback = {
12266                 tag: 'span',
12267                 cls: 'glyphicon form-control-feedback'
12268             };
12269             
12270             if(this.removable && !this.editable  ){
12271                 inputblock = {
12272                     cls : 'has-feedback',
12273                     cn :  [
12274                         inputblock,
12275                         {
12276                             tag: 'button',
12277                             html : 'x',
12278                             cls : 'roo-combo-removable-btn close'
12279                         },
12280                         feedback
12281                     ] 
12282                 };
12283             } else {
12284                 inputblock = {
12285                     cls : 'has-feedback',
12286                     cn :  [
12287                         inputblock,
12288                         feedback
12289                     ] 
12290                 };
12291             }
12292
12293         } else {
12294             if(this.removable && !this.editable ){
12295                 inputblock = {
12296                     cls : 'roo-removable',
12297                     cn :  [
12298                         inputblock,
12299                         {
12300                             tag: 'button',
12301                             html : 'x',
12302                             cls : 'roo-combo-removable-btn close'
12303                         }
12304                     ] 
12305                 };
12306             }
12307         }
12308         
12309         if (this.before || this.after) {
12310             
12311             inputblock = {
12312                 cls : 'input-group',
12313                 cn :  [] 
12314             };
12315             if (this.before) {
12316                 inputblock.cn.push({
12317                     tag :'span',
12318                     cls : 'input-group-addon input-group-prepend input-group-text',
12319                     html : this.before
12320                 });
12321             }
12322             
12323             inputblock.cn.push(input);
12324             
12325             if(this.hasFeedback && !this.allowBlank){
12326                 inputblock.cls += ' has-feedback';
12327                 inputblock.cn.push(feedback);
12328             }
12329             
12330             if (this.after) {
12331                 inputblock.cn.push({
12332                     tag :'span',
12333                     cls : 'input-group-addon input-group-append input-group-text',
12334                     html : this.after
12335                 });
12336             }
12337             
12338         };
12339         
12340       
12341         
12342         var ibwrap = inputblock;
12343         
12344         if(this.multiple){
12345             ibwrap = {
12346                 tag: 'ul',
12347                 cls: 'roo-select2-choices',
12348                 cn:[
12349                     {
12350                         tag: 'li',
12351                         cls: 'roo-select2-search-field',
12352                         cn: [
12353
12354                             inputblock
12355                         ]
12356                     }
12357                 ]
12358             };
12359                 
12360         }
12361         
12362         var combobox = {
12363             cls: 'roo-select2-container input-group',
12364             cn: [
12365                  {
12366                     tag: 'input',
12367                     type : 'hidden',
12368                     cls: 'form-hidden-field'
12369                 },
12370                 ibwrap
12371             ]
12372         };
12373         
12374         if(!this.multiple && this.showToggleBtn){
12375             
12376             var caret = {
12377                         tag: 'span',
12378                         cls: 'caret'
12379              };
12380             if (this.caret != false) {
12381                 caret = {
12382                      tag: 'i',
12383                      cls: 'fa fa-' + this.caret
12384                 };
12385                 
12386             }
12387             
12388             combobox.cn.push({
12389                 tag :'span',
12390                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12391                 cn : [
12392                     Roo.bootstrap.version == 3 ? caret : '',
12393                     {
12394                         tag: 'span',
12395                         cls: 'combobox-clear',
12396                         cn  : [
12397                             {
12398                                 tag : 'i',
12399                                 cls: 'icon-remove'
12400                             }
12401                         ]
12402                     }
12403                 ]
12404
12405             })
12406         }
12407         
12408         if(this.multiple){
12409             combobox.cls += ' roo-select2-container-multi';
12410         }
12411          var indicator = {
12412             tag : 'i',
12413             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12414             tooltip : 'This field is required'
12415         };
12416         if (Roo.bootstrap.version == 4) {
12417             indicator = {
12418                 tag : 'i',
12419                 style : 'display:none'
12420             };
12421         }
12422         
12423         
12424         if (align ==='left' && this.fieldLabel.length) {
12425             
12426             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12427
12428             cfg.cn = [
12429                 indicator,
12430                 {
12431                     tag: 'label',
12432                     'for' :  id,
12433                     cls : 'control-label',
12434                     html : this.fieldLabel
12435
12436                 },
12437                 {
12438                     cls : "", 
12439                     cn: [
12440                         combobox
12441                     ]
12442                 }
12443
12444             ];
12445             
12446             var labelCfg = cfg.cn[1];
12447             var contentCfg = cfg.cn[2];
12448             
12449             if(this.indicatorpos == 'right'){
12450                 cfg.cn = [
12451                     {
12452                         tag: 'label',
12453                         'for' :  id,
12454                         cls : 'control-label',
12455                         cn : [
12456                             {
12457                                 tag : 'span',
12458                                 html : this.fieldLabel
12459                             },
12460                             indicator
12461                         ]
12462                     },
12463                     {
12464                         cls : "", 
12465                         cn: [
12466                             combobox
12467                         ]
12468                     }
12469
12470                 ];
12471                 
12472                 labelCfg = cfg.cn[0];
12473                 contentCfg = cfg.cn[1];
12474             }
12475             
12476             if(this.labelWidth > 12){
12477                 labelCfg.style = "width: " + this.labelWidth + 'px';
12478             }
12479             
12480             if(this.labelWidth < 13 && this.labelmd == 0){
12481                 this.labelmd = this.labelWidth;
12482             }
12483             
12484             if(this.labellg > 0){
12485                 labelCfg.cls += ' col-lg-' + this.labellg;
12486                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12487             }
12488             
12489             if(this.labelmd > 0){
12490                 labelCfg.cls += ' col-md-' + this.labelmd;
12491                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12492             }
12493             
12494             if(this.labelsm > 0){
12495                 labelCfg.cls += ' col-sm-' + this.labelsm;
12496                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12497             }
12498             
12499             if(this.labelxs > 0){
12500                 labelCfg.cls += ' col-xs-' + this.labelxs;
12501                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12502             }
12503             
12504         } else if ( this.fieldLabel.length) {
12505 //                Roo.log(" label");
12506             cfg.cn = [
12507                 indicator,
12508                {
12509                    tag: 'label',
12510                    //cls : 'input-group-addon',
12511                    html : this.fieldLabel
12512
12513                },
12514
12515                combobox
12516
12517             ];
12518             
12519             if(this.indicatorpos == 'right'){
12520                 
12521                 cfg.cn = [
12522                     {
12523                        tag: 'label',
12524                        cn : [
12525                            {
12526                                tag : 'span',
12527                                html : this.fieldLabel
12528                            },
12529                            indicator
12530                        ]
12531
12532                     },
12533                     combobox
12534
12535                 ];
12536
12537             }
12538
12539         } else {
12540             
12541 //                Roo.log(" no label && no align");
12542                 cfg = combobox
12543                      
12544                 
12545         }
12546         
12547         var settings=this;
12548         ['xs','sm','md','lg'].map(function(size){
12549             if (settings[size]) {
12550                 cfg.cls += ' col-' + size + '-' + settings[size];
12551             }
12552         });
12553         
12554         return cfg;
12555         
12556     },
12557     
12558     
12559     
12560     // private
12561     onResize : function(w, h){
12562 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12563 //        if(typeof w == 'number'){
12564 //            var x = w - this.trigger.getWidth();
12565 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12566 //            this.trigger.setStyle('left', x+'px');
12567 //        }
12568     },
12569
12570     // private
12571     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12572
12573     // private
12574     getResizeEl : function(){
12575         return this.inputEl();
12576     },
12577
12578     // private
12579     getPositionEl : function(){
12580         return this.inputEl();
12581     },
12582
12583     // private
12584     alignErrorIcon : function(){
12585         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12586     },
12587
12588     // private
12589     initEvents : function(){
12590         
12591         this.createList();
12592         
12593         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12594         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12595         if(!this.multiple && this.showToggleBtn){
12596             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12597             if(this.hideTrigger){
12598                 this.trigger.setDisplayed(false);
12599             }
12600             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12601         }
12602         
12603         if(this.multiple){
12604             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12605         }
12606         
12607         if(this.removable && !this.editable && !this.tickable){
12608             var close = this.closeTriggerEl();
12609             
12610             if(close){
12611                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12612                 close.on('click', this.removeBtnClick, this, close);
12613             }
12614         }
12615         
12616         //this.trigger.addClassOnOver('x-form-trigger-over');
12617         //this.trigger.addClassOnClick('x-form-trigger-click');
12618         
12619         //if(!this.width){
12620         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12621         //}
12622     },
12623     
12624     closeTriggerEl : function()
12625     {
12626         var close = this.el.select('.roo-combo-removable-btn', true).first();
12627         return close ? close : false;
12628     },
12629     
12630     removeBtnClick : function(e, h, el)
12631     {
12632         e.preventDefault();
12633         
12634         if(this.fireEvent("remove", this) !== false){
12635             this.reset();
12636             this.fireEvent("afterremove", this)
12637         }
12638     },
12639     
12640     createList : function()
12641     {
12642         this.list = Roo.get(document.body).createChild({
12643             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12644             cls: 'typeahead typeahead-long dropdown-menu shadow',
12645             style: 'display:none'
12646         });
12647         
12648         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12649         
12650     },
12651
12652     // private
12653     initTrigger : function(){
12654        
12655     },
12656
12657     // private
12658     onDestroy : function(){
12659         if(this.trigger){
12660             this.trigger.removeAllListeners();
12661           //  this.trigger.remove();
12662         }
12663         //if(this.wrap){
12664         //    this.wrap.remove();
12665         //}
12666         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12667     },
12668
12669     // private
12670     onFocus : function(){
12671         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12672         /*
12673         if(!this.mimicing){
12674             this.wrap.addClass('x-trigger-wrap-focus');
12675             this.mimicing = true;
12676             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12677             if(this.monitorTab){
12678                 this.el.on("keydown", this.checkTab, this);
12679             }
12680         }
12681         */
12682     },
12683
12684     // private
12685     checkTab : function(e){
12686         if(e.getKey() == e.TAB){
12687             this.triggerBlur();
12688         }
12689     },
12690
12691     // private
12692     onBlur : function(){
12693         // do nothing
12694     },
12695
12696     // private
12697     mimicBlur : function(e, t){
12698         /*
12699         if(!this.wrap.contains(t) && this.validateBlur()){
12700             this.triggerBlur();
12701         }
12702         */
12703     },
12704
12705     // private
12706     triggerBlur : function(){
12707         this.mimicing = false;
12708         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12709         if(this.monitorTab){
12710             this.el.un("keydown", this.checkTab, this);
12711         }
12712         //this.wrap.removeClass('x-trigger-wrap-focus');
12713         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12714     },
12715
12716     // private
12717     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12718     validateBlur : function(e, t){
12719         return true;
12720     },
12721
12722     // private
12723     onDisable : function(){
12724         this.inputEl().dom.disabled = true;
12725         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12726         //if(this.wrap){
12727         //    this.wrap.addClass('x-item-disabled');
12728         //}
12729     },
12730
12731     // private
12732     onEnable : function(){
12733         this.inputEl().dom.disabled = false;
12734         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12735         //if(this.wrap){
12736         //    this.el.removeClass('x-item-disabled');
12737         //}
12738     },
12739
12740     // private
12741     onShow : function(){
12742         var ae = this.getActionEl();
12743         
12744         if(ae){
12745             ae.dom.style.display = '';
12746             ae.dom.style.visibility = 'visible';
12747         }
12748     },
12749
12750     // private
12751     
12752     onHide : function(){
12753         var ae = this.getActionEl();
12754         ae.dom.style.display = 'none';
12755     },
12756
12757     /**
12758      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12759      * by an implementing function.
12760      * @method
12761      * @param {EventObject} e
12762      */
12763     onTriggerClick : Roo.emptyFn
12764 });
12765  
12766 /*
12767 * Licence: LGPL
12768 */
12769
12770 /**
12771  * @class Roo.bootstrap.CardUploader
12772  * @extends Roo.bootstrap.Button
12773  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12774  * @cfg {Number} errorTimeout default 3000
12775  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12776  * @cfg {Array}  html The button text.
12777
12778  *
12779  * @constructor
12780  * Create a new CardUploader
12781  * @param {Object} config The config object
12782  */
12783
12784 Roo.bootstrap.CardUploader = function(config){
12785     
12786  
12787     
12788     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12789     
12790     
12791     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12792         return r.data.id
12793      });
12794     
12795      this.addEvents({
12796          // raw events
12797         /**
12798          * @event preview
12799          * When a image is clicked on - and needs to display a slideshow or similar..
12800          * @param {Roo.bootstrap.Card} this
12801          * @param {Object} The image information data 
12802          *
12803          */
12804         'preview' : true,
12805          /**
12806          * @event download
12807          * When a the download link is clicked
12808          * @param {Roo.bootstrap.Card} this
12809          * @param {Object} The image information data  contains 
12810          */
12811         'download' : true
12812         
12813     });
12814 };
12815  
12816 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12817     
12818      
12819     errorTimeout : 3000,
12820      
12821     images : false,
12822    
12823     fileCollection : false,
12824     allowBlank : true,
12825     
12826     getAutoCreate : function()
12827     {
12828         
12829         var cfg =  {
12830             cls :'form-group' ,
12831             cn : [
12832                
12833                 {
12834                     tag: 'label',
12835                    //cls : 'input-group-addon',
12836                     html : this.fieldLabel
12837
12838                 },
12839
12840                 {
12841                     tag: 'input',
12842                     type : 'hidden',
12843                     name : this.name,
12844                     value : this.value,
12845                     cls : 'd-none  form-control'
12846                 },
12847                 
12848                 {
12849                     tag: 'input',
12850                     multiple : 'multiple',
12851                     type : 'file',
12852                     cls : 'd-none  roo-card-upload-selector'
12853                 },
12854                 
12855                 {
12856                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12857                 },
12858                 {
12859                     cls : 'card-columns roo-card-uploader-container'
12860                 }
12861
12862             ]
12863         };
12864            
12865          
12866         return cfg;
12867     },
12868     
12869     getChildContainer : function() /// what children are added to.
12870     {
12871         return this.containerEl;
12872     },
12873    
12874     getButtonContainer : function() /// what children are added to.
12875     {
12876         return this.el.select(".roo-card-uploader-button-container").first();
12877     },
12878    
12879     initEvents : function()
12880     {
12881         
12882         Roo.bootstrap.Input.prototype.initEvents.call(this);
12883         
12884         var t = this;
12885         this.addxtype({
12886             xns: Roo.bootstrap,
12887
12888             xtype : 'Button',
12889             container_method : 'getButtonContainer' ,            
12890             html :  this.html, // fix changable?
12891             cls : 'w-100 ',
12892             listeners : {
12893                 'click' : function(btn, e) {
12894                     t.onClick(e);
12895                 }
12896             }
12897         });
12898         
12899         
12900         
12901         
12902         this.urlAPI = (window.createObjectURL && window) || 
12903                                 (window.URL && URL.revokeObjectURL && URL) || 
12904                                 (window.webkitURL && webkitURL);
12905                         
12906          
12907          
12908          
12909         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12910         
12911         this.selectorEl.on('change', this.onFileSelected, this);
12912         if (this.images) {
12913             var t = this;
12914             this.images.forEach(function(img) {
12915                 t.addCard(img)
12916             });
12917             this.images = false;
12918         }
12919         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12920          
12921        
12922     },
12923     
12924    
12925     onClick : function(e)
12926     {
12927         e.preventDefault();
12928          
12929         this.selectorEl.dom.click();
12930          
12931     },
12932     
12933     onFileSelected : function(e)
12934     {
12935         e.preventDefault();
12936         
12937         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12938             return;
12939         }
12940         
12941         Roo.each(this.selectorEl.dom.files, function(file){    
12942             this.addFile(file);
12943         }, this);
12944          
12945     },
12946     
12947       
12948     
12949       
12950     
12951     addFile : function(file)
12952     {
12953            
12954         if(typeof(file) === 'string'){
12955             throw "Add file by name?"; // should not happen
12956             return;
12957         }
12958         
12959         if(!file || !this.urlAPI){
12960             return;
12961         }
12962         
12963         // file;
12964         // file.type;
12965         
12966         var _this = this;
12967         
12968         
12969         var url = _this.urlAPI.createObjectURL( file);
12970            
12971         this.addCard({
12972             id : Roo.bootstrap.CardUploader.ID--,
12973             is_uploaded : false,
12974             src : url,
12975             srcfile : file,
12976             title : file.name,
12977             mimetype : file.type,
12978             preview : false,
12979             is_deleted : 0
12980         });
12981         
12982     },
12983     
12984     /**
12985      * addCard - add an Attachment to the uploader
12986      * @param data - the data about the image to upload
12987      *
12988      * {
12989           id : 123
12990           title : "Title of file",
12991           is_uploaded : false,
12992           src : "http://.....",
12993           srcfile : { the File upload object },
12994           mimetype : file.type,
12995           preview : false,
12996           is_deleted : 0
12997           .. any other data...
12998         }
12999      *
13000      * 
13001     */
13002     
13003     addCard : function (data)
13004     {
13005         // hidden input element?
13006         // if the file is not an image...
13007         //then we need to use something other that and header_image
13008         var t = this;
13009         //   remove.....
13010         var footer = [
13011             {
13012                 xns : Roo.bootstrap,
13013                 xtype : 'CardFooter',
13014                  items: [
13015                     {
13016                         xns : Roo.bootstrap,
13017                         xtype : 'Element',
13018                         cls : 'd-flex',
13019                         items : [
13020                             
13021                             {
13022                                 xns : Roo.bootstrap,
13023                                 xtype : 'Button',
13024                                 html : String.format("<small>{0}</small>", data.title),
13025                                 cls : 'col-10 text-left',
13026                                 size: 'sm',
13027                                 weight: 'link',
13028                                 fa : 'download',
13029                                 listeners : {
13030                                     click : function() {
13031                                      
13032                                         t.fireEvent( "download", t, data );
13033                                     }
13034                                 }
13035                             },
13036                           
13037                             {
13038                                 xns : Roo.bootstrap,
13039                                 xtype : 'Button',
13040                                 style: 'max-height: 28px; ',
13041                                 size : 'sm',
13042                                 weight: 'danger',
13043                                 cls : 'col-2',
13044                                 fa : 'times',
13045                                 listeners : {
13046                                     click : function() {
13047                                         t.removeCard(data.id)
13048                                     }
13049                                 }
13050                             }
13051                         ]
13052                     }
13053                     
13054                 ] 
13055             }
13056             
13057         ];
13058         
13059         var cn = this.addxtype(
13060             {
13061                  
13062                 xns : Roo.bootstrap,
13063                 xtype : 'Card',
13064                 closeable : true,
13065                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13066                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13067                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13068                 data : data,
13069                 html : false,
13070                  
13071                 items : footer,
13072                 initEvents : function() {
13073                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13074                     var card = this;
13075                     this.imgEl = this.el.select('.card-img-top').first();
13076                     if (this.imgEl) {
13077                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13078                         this.imgEl.set({ 'pointer' : 'cursor' });
13079                                   
13080                     }
13081                     this.getCardFooter().addClass('p-1');
13082                     
13083                   
13084                 }
13085                 
13086             }
13087         );
13088         // dont' really need ot update items.
13089         // this.items.push(cn);
13090         this.fileCollection.add(cn);
13091         
13092         if (!data.srcfile) {
13093             this.updateInput();
13094             return;
13095         }
13096             
13097         var _t = this;
13098         var reader = new FileReader();
13099         reader.addEventListener("load", function() {  
13100             data.srcdata =  reader.result;
13101             _t.updateInput();
13102         });
13103         reader.readAsDataURL(data.srcfile);
13104         
13105         
13106         
13107     },
13108     removeCard : function(id)
13109     {
13110         
13111         var card  = this.fileCollection.get(id);
13112         card.data.is_deleted = 1;
13113         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13114         //this.fileCollection.remove(card);
13115         //this.items = this.items.filter(function(e) { return e != card });
13116         // dont' really need ot update items.
13117         card.el.dom.parentNode.removeChild(card.el.dom);
13118         this.updateInput();
13119
13120         
13121     },
13122     reset: function()
13123     {
13124         this.fileCollection.each(function(card) {
13125             if (card.el.dom && card.el.dom.parentNode) {
13126                 card.el.dom.parentNode.removeChild(card.el.dom);
13127             }
13128         });
13129         this.fileCollection.clear();
13130         this.updateInput();
13131     },
13132     
13133     updateInput : function()
13134     {
13135          var data = [];
13136         this.fileCollection.each(function(e) {
13137             data.push(e.data);
13138             
13139         });
13140         this.inputEl().dom.value = JSON.stringify(data);
13141         
13142         
13143         
13144     }
13145     
13146     
13147 });
13148
13149
13150 Roo.bootstrap.CardUploader.ID = -1;/*
13151  * Based on:
13152  * Ext JS Library 1.1.1
13153  * Copyright(c) 2006-2007, Ext JS, LLC.
13154  *
13155  * Originally Released Under LGPL - original licence link has changed is not relivant.
13156  *
13157  * Fork - LGPL
13158  * <script type="text/javascript">
13159  */
13160
13161
13162 /**
13163  * @class Roo.data.SortTypes
13164  * @singleton
13165  * Defines the default sorting (casting?) comparison functions used when sorting data.
13166  */
13167 Roo.data.SortTypes = {
13168     /**
13169      * Default sort that does nothing
13170      * @param {Mixed} s The value being converted
13171      * @return {Mixed} The comparison value
13172      */
13173     none : function(s){
13174         return s;
13175     },
13176     
13177     /**
13178      * The regular expression used to strip tags
13179      * @type {RegExp}
13180      * @property
13181      */
13182     stripTagsRE : /<\/?[^>]+>/gi,
13183     
13184     /**
13185      * Strips all HTML tags to sort on text only
13186      * @param {Mixed} s The value being converted
13187      * @return {String} The comparison value
13188      */
13189     asText : function(s){
13190         return String(s).replace(this.stripTagsRE, "");
13191     },
13192     
13193     /**
13194      * Strips all HTML tags to sort on text only - Case insensitive
13195      * @param {Mixed} s The value being converted
13196      * @return {String} The comparison value
13197      */
13198     asUCText : function(s){
13199         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13200     },
13201     
13202     /**
13203      * Case insensitive string
13204      * @param {Mixed} s The value being converted
13205      * @return {String} The comparison value
13206      */
13207     asUCString : function(s) {
13208         return String(s).toUpperCase();
13209     },
13210     
13211     /**
13212      * Date sorting
13213      * @param {Mixed} s The value being converted
13214      * @return {Number} The comparison value
13215      */
13216     asDate : function(s) {
13217         if(!s){
13218             return 0;
13219         }
13220         if(s instanceof Date){
13221             return s.getTime();
13222         }
13223         return Date.parse(String(s));
13224     },
13225     
13226     /**
13227      * Float sorting
13228      * @param {Mixed} s The value being converted
13229      * @return {Float} The comparison value
13230      */
13231     asFloat : function(s) {
13232         var val = parseFloat(String(s).replace(/,/g, ""));
13233         if(isNaN(val)) {
13234             val = 0;
13235         }
13236         return val;
13237     },
13238     
13239     /**
13240      * Integer sorting
13241      * @param {Mixed} s The value being converted
13242      * @return {Number} The comparison value
13243      */
13244     asInt : function(s) {
13245         var val = parseInt(String(s).replace(/,/g, ""));
13246         if(isNaN(val)) {
13247             val = 0;
13248         }
13249         return val;
13250     }
13251 };/*
13252  * Based on:
13253  * Ext JS Library 1.1.1
13254  * Copyright(c) 2006-2007, Ext JS, LLC.
13255  *
13256  * Originally Released Under LGPL - original licence link has changed is not relivant.
13257  *
13258  * Fork - LGPL
13259  * <script type="text/javascript">
13260  */
13261
13262 /**
13263 * @class Roo.data.Record
13264  * Instances of this class encapsulate both record <em>definition</em> information, and record
13265  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13266  * to access Records cached in an {@link Roo.data.Store} object.<br>
13267  * <p>
13268  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13269  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13270  * objects.<br>
13271  * <p>
13272  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13273  * @constructor
13274  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13275  * {@link #create}. The parameters are the same.
13276  * @param {Array} data An associative Array of data values keyed by the field name.
13277  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13278  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13279  * not specified an integer id is generated.
13280  */
13281 Roo.data.Record = function(data, id){
13282     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13283     this.data = data;
13284 };
13285
13286 /**
13287  * Generate a constructor for a specific record layout.
13288  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13289  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13290  * Each field definition object may contain the following properties: <ul>
13291  * <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,
13292  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13293  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13294  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13295  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13296  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13297  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13298  * this may be omitted.</p></li>
13299  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13300  * <ul><li>auto (Default, implies no conversion)</li>
13301  * <li>string</li>
13302  * <li>int</li>
13303  * <li>float</li>
13304  * <li>boolean</li>
13305  * <li>date</li></ul></p></li>
13306  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13307  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13308  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13309  * by the Reader into an object that will be stored in the Record. It is passed the
13310  * following parameters:<ul>
13311  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13312  * </ul></p></li>
13313  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13314  * </ul>
13315  * <br>usage:<br><pre><code>
13316 var TopicRecord = Roo.data.Record.create(
13317     {name: 'title', mapping: 'topic_title'},
13318     {name: 'author', mapping: 'username'},
13319     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13320     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13321     {name: 'lastPoster', mapping: 'user2'},
13322     {name: 'excerpt', mapping: 'post_text'}
13323 );
13324
13325 var myNewRecord = new TopicRecord({
13326     title: 'Do my job please',
13327     author: 'noobie',
13328     totalPosts: 1,
13329     lastPost: new Date(),
13330     lastPoster: 'Animal',
13331     excerpt: 'No way dude!'
13332 });
13333 myStore.add(myNewRecord);
13334 </code></pre>
13335  * @method create
13336  * @static
13337  */
13338 Roo.data.Record.create = function(o){
13339     var f = function(){
13340         f.superclass.constructor.apply(this, arguments);
13341     };
13342     Roo.extend(f, Roo.data.Record);
13343     var p = f.prototype;
13344     p.fields = new Roo.util.MixedCollection(false, function(field){
13345         return field.name;
13346     });
13347     for(var i = 0, len = o.length; i < len; i++){
13348         p.fields.add(new Roo.data.Field(o[i]));
13349     }
13350     f.getField = function(name){
13351         return p.fields.get(name);  
13352     };
13353     return f;
13354 };
13355
13356 Roo.data.Record.AUTO_ID = 1000;
13357 Roo.data.Record.EDIT = 'edit';
13358 Roo.data.Record.REJECT = 'reject';
13359 Roo.data.Record.COMMIT = 'commit';
13360
13361 Roo.data.Record.prototype = {
13362     /**
13363      * Readonly flag - true if this record has been modified.
13364      * @type Boolean
13365      */
13366     dirty : false,
13367     editing : false,
13368     error: null,
13369     modified: null,
13370
13371     // private
13372     join : function(store){
13373         this.store = store;
13374     },
13375
13376     /**
13377      * Set the named field to the specified value.
13378      * @param {String} name The name of the field to set.
13379      * @param {Object} value The value to set the field to.
13380      */
13381     set : function(name, value){
13382         if(this.data[name] == value){
13383             return;
13384         }
13385         this.dirty = true;
13386         if(!this.modified){
13387             this.modified = {};
13388         }
13389         if(typeof this.modified[name] == 'undefined'){
13390             this.modified[name] = this.data[name];
13391         }
13392         this.data[name] = value;
13393         if(!this.editing && this.store){
13394             this.store.afterEdit(this);
13395         }       
13396     },
13397
13398     /**
13399      * Get the value of the named field.
13400      * @param {String} name The name of the field to get the value of.
13401      * @return {Object} The value of the field.
13402      */
13403     get : function(name){
13404         return this.data[name]; 
13405     },
13406
13407     // private
13408     beginEdit : function(){
13409         this.editing = true;
13410         this.modified = {}; 
13411     },
13412
13413     // private
13414     cancelEdit : function(){
13415         this.editing = false;
13416         delete this.modified;
13417     },
13418
13419     // private
13420     endEdit : function(){
13421         this.editing = false;
13422         if(this.dirty && this.store){
13423             this.store.afterEdit(this);
13424         }
13425     },
13426
13427     /**
13428      * Usually called by the {@link Roo.data.Store} which owns the Record.
13429      * Rejects all changes made to the Record since either creation, or the last commit operation.
13430      * Modified fields are reverted to their original values.
13431      * <p>
13432      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13433      * of reject operations.
13434      */
13435     reject : function(){
13436         var m = this.modified;
13437         for(var n in m){
13438             if(typeof m[n] != "function"){
13439                 this.data[n] = m[n];
13440             }
13441         }
13442         this.dirty = false;
13443         delete this.modified;
13444         this.editing = false;
13445         if(this.store){
13446             this.store.afterReject(this);
13447         }
13448     },
13449
13450     /**
13451      * Usually called by the {@link Roo.data.Store} which owns the Record.
13452      * Commits all changes made to the Record since either creation, or the last commit operation.
13453      * <p>
13454      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13455      * of commit operations.
13456      */
13457     commit : function(){
13458         this.dirty = false;
13459         delete this.modified;
13460         this.editing = false;
13461         if(this.store){
13462             this.store.afterCommit(this);
13463         }
13464     },
13465
13466     // private
13467     hasError : function(){
13468         return this.error != null;
13469     },
13470
13471     // private
13472     clearError : function(){
13473         this.error = null;
13474     },
13475
13476     /**
13477      * Creates a copy of this record.
13478      * @param {String} id (optional) A new record id if you don't want to use this record's id
13479      * @return {Record}
13480      */
13481     copy : function(newId) {
13482         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13483     }
13484 };/*
13485  * Based on:
13486  * Ext JS Library 1.1.1
13487  * Copyright(c) 2006-2007, Ext JS, LLC.
13488  *
13489  * Originally Released Under LGPL - original licence link has changed is not relivant.
13490  *
13491  * Fork - LGPL
13492  * <script type="text/javascript">
13493  */
13494
13495
13496
13497 /**
13498  * @class Roo.data.Store
13499  * @extends Roo.util.Observable
13500  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13501  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13502  * <p>
13503  * 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
13504  * has no knowledge of the format of the data returned by the Proxy.<br>
13505  * <p>
13506  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13507  * instances from the data object. These records are cached and made available through accessor functions.
13508  * @constructor
13509  * Creates a new Store.
13510  * @param {Object} config A config object containing the objects needed for the Store to access data,
13511  * and read the data into Records.
13512  */
13513 Roo.data.Store = function(config){
13514     this.data = new Roo.util.MixedCollection(false);
13515     this.data.getKey = function(o){
13516         return o.id;
13517     };
13518     this.baseParams = {};
13519     // private
13520     this.paramNames = {
13521         "start" : "start",
13522         "limit" : "limit",
13523         "sort" : "sort",
13524         "dir" : "dir",
13525         "multisort" : "_multisort"
13526     };
13527
13528     if(config && config.data){
13529         this.inlineData = config.data;
13530         delete config.data;
13531     }
13532
13533     Roo.apply(this, config);
13534     
13535     if(this.reader){ // reader passed
13536         this.reader = Roo.factory(this.reader, Roo.data);
13537         this.reader.xmodule = this.xmodule || false;
13538         if(!this.recordType){
13539             this.recordType = this.reader.recordType;
13540         }
13541         if(this.reader.onMetaChange){
13542             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13543         }
13544     }
13545
13546     if(this.recordType){
13547         this.fields = this.recordType.prototype.fields;
13548     }
13549     this.modified = [];
13550
13551     this.addEvents({
13552         /**
13553          * @event datachanged
13554          * Fires when the data cache has changed, and a widget which is using this Store
13555          * as a Record cache should refresh its view.
13556          * @param {Store} this
13557          */
13558         datachanged : true,
13559         /**
13560          * @event metachange
13561          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13562          * @param {Store} this
13563          * @param {Object} meta The JSON metadata
13564          */
13565         metachange : true,
13566         /**
13567          * @event add
13568          * Fires when Records have been added to the Store
13569          * @param {Store} this
13570          * @param {Roo.data.Record[]} records The array of Records added
13571          * @param {Number} index The index at which the record(s) were added
13572          */
13573         add : true,
13574         /**
13575          * @event remove
13576          * Fires when a Record has been removed from the Store
13577          * @param {Store} this
13578          * @param {Roo.data.Record} record The Record that was removed
13579          * @param {Number} index The index at which the record was removed
13580          */
13581         remove : true,
13582         /**
13583          * @event update
13584          * Fires when a Record has been updated
13585          * @param {Store} this
13586          * @param {Roo.data.Record} record The Record that was updated
13587          * @param {String} operation The update operation being performed.  Value may be one of:
13588          * <pre><code>
13589  Roo.data.Record.EDIT
13590  Roo.data.Record.REJECT
13591  Roo.data.Record.COMMIT
13592          * </code></pre>
13593          */
13594         update : true,
13595         /**
13596          * @event clear
13597          * Fires when the data cache has been cleared.
13598          * @param {Store} this
13599          */
13600         clear : true,
13601         /**
13602          * @event beforeload
13603          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13604          * the load action will be canceled.
13605          * @param {Store} this
13606          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13607          */
13608         beforeload : true,
13609         /**
13610          * @event beforeloadadd
13611          * Fires after a new set of Records has been loaded.
13612          * @param {Store} this
13613          * @param {Roo.data.Record[]} records The Records that were loaded
13614          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13615          */
13616         beforeloadadd : true,
13617         /**
13618          * @event load
13619          * Fires after a new set of Records has been loaded, before they are added to the store.
13620          * @param {Store} this
13621          * @param {Roo.data.Record[]} records The Records that were loaded
13622          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13623          * @params {Object} return from reader
13624          */
13625         load : true,
13626         /**
13627          * @event loadexception
13628          * Fires if an exception occurs in the Proxy during loading.
13629          * Called with the signature of the Proxy's "loadexception" event.
13630          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13631          * 
13632          * @param {Proxy} 
13633          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13634          * @param {Object} load options 
13635          * @param {Object} jsonData from your request (normally this contains the Exception)
13636          */
13637         loadexception : true
13638     });
13639     
13640     if(this.proxy){
13641         this.proxy = Roo.factory(this.proxy, Roo.data);
13642         this.proxy.xmodule = this.xmodule || false;
13643         this.relayEvents(this.proxy,  ["loadexception"]);
13644     }
13645     this.sortToggle = {};
13646     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13647
13648     Roo.data.Store.superclass.constructor.call(this);
13649
13650     if(this.inlineData){
13651         this.loadData(this.inlineData);
13652         delete this.inlineData;
13653     }
13654 };
13655
13656 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13657      /**
13658     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13659     * without a remote query - used by combo/forms at present.
13660     */
13661     
13662     /**
13663     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13664     */
13665     /**
13666     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13667     */
13668     /**
13669     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13670     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13671     */
13672     /**
13673     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13674     * on any HTTP request
13675     */
13676     /**
13677     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13678     */
13679     /**
13680     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13681     */
13682     multiSort: false,
13683     /**
13684     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13685     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13686     */
13687     remoteSort : false,
13688
13689     /**
13690     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13691      * loaded or when a record is removed. (defaults to false).
13692     */
13693     pruneModifiedRecords : false,
13694
13695     // private
13696     lastOptions : null,
13697
13698     /**
13699      * Add Records to the Store and fires the add event.
13700      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13701      */
13702     add : function(records){
13703         records = [].concat(records);
13704         for(var i = 0, len = records.length; i < len; i++){
13705             records[i].join(this);
13706         }
13707         var index = this.data.length;
13708         this.data.addAll(records);
13709         this.fireEvent("add", this, records, index);
13710     },
13711
13712     /**
13713      * Remove a Record from the Store and fires the remove event.
13714      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13715      */
13716     remove : function(record){
13717         var index = this.data.indexOf(record);
13718         this.data.removeAt(index);
13719  
13720         if(this.pruneModifiedRecords){
13721             this.modified.remove(record);
13722         }
13723         this.fireEvent("remove", this, record, index);
13724     },
13725
13726     /**
13727      * Remove all Records from the Store and fires the clear event.
13728      */
13729     removeAll : function(){
13730         this.data.clear();
13731         if(this.pruneModifiedRecords){
13732             this.modified = [];
13733         }
13734         this.fireEvent("clear", this);
13735     },
13736
13737     /**
13738      * Inserts Records to the Store at the given index and fires the add event.
13739      * @param {Number} index The start index at which to insert the passed Records.
13740      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13741      */
13742     insert : function(index, records){
13743         records = [].concat(records);
13744         for(var i = 0, len = records.length; i < len; i++){
13745             this.data.insert(index, records[i]);
13746             records[i].join(this);
13747         }
13748         this.fireEvent("add", this, records, index);
13749     },
13750
13751     /**
13752      * Get the index within the cache of the passed Record.
13753      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13754      * @return {Number} The index of the passed Record. Returns -1 if not found.
13755      */
13756     indexOf : function(record){
13757         return this.data.indexOf(record);
13758     },
13759
13760     /**
13761      * Get the index within the cache of the Record with the passed id.
13762      * @param {String} id The id of the Record to find.
13763      * @return {Number} The index of the Record. Returns -1 if not found.
13764      */
13765     indexOfId : function(id){
13766         return this.data.indexOfKey(id);
13767     },
13768
13769     /**
13770      * Get the Record with the specified id.
13771      * @param {String} id The id of the Record to find.
13772      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13773      */
13774     getById : function(id){
13775         return this.data.key(id);
13776     },
13777
13778     /**
13779      * Get the Record at the specified index.
13780      * @param {Number} index The index of the Record to find.
13781      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13782      */
13783     getAt : function(index){
13784         return this.data.itemAt(index);
13785     },
13786
13787     /**
13788      * Returns a range of Records between specified indices.
13789      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13790      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13791      * @return {Roo.data.Record[]} An array of Records
13792      */
13793     getRange : function(start, end){
13794         return this.data.getRange(start, end);
13795     },
13796
13797     // private
13798     storeOptions : function(o){
13799         o = Roo.apply({}, o);
13800         delete o.callback;
13801         delete o.scope;
13802         this.lastOptions = o;
13803     },
13804
13805     /**
13806      * Loads the Record cache from the configured Proxy using the configured Reader.
13807      * <p>
13808      * If using remote paging, then the first load call must specify the <em>start</em>
13809      * and <em>limit</em> properties in the options.params property to establish the initial
13810      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13811      * <p>
13812      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13813      * and this call will return before the new data has been loaded. Perform any post-processing
13814      * in a callback function, or in a "load" event handler.</strong>
13815      * <p>
13816      * @param {Object} options An object containing properties which control loading options:<ul>
13817      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13818      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13819      * passed the following arguments:<ul>
13820      * <li>r : Roo.data.Record[]</li>
13821      * <li>options: Options object from the load call</li>
13822      * <li>success: Boolean success indicator</li></ul></li>
13823      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13824      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13825      * </ul>
13826      */
13827     load : function(options){
13828         options = options || {};
13829         if(this.fireEvent("beforeload", this, options) !== false){
13830             this.storeOptions(options);
13831             var p = Roo.apply(options.params || {}, this.baseParams);
13832             // if meta was not loaded from remote source.. try requesting it.
13833             if (!this.reader.metaFromRemote) {
13834                 p._requestMeta = 1;
13835             }
13836             if(this.sortInfo && this.remoteSort){
13837                 var pn = this.paramNames;
13838                 p[pn["sort"]] = this.sortInfo.field;
13839                 p[pn["dir"]] = this.sortInfo.direction;
13840             }
13841             if (this.multiSort) {
13842                 var pn = this.paramNames;
13843                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13844             }
13845             
13846             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13847         }
13848     },
13849
13850     /**
13851      * Reloads the Record cache from the configured Proxy using the configured Reader and
13852      * the options from the last load operation performed.
13853      * @param {Object} options (optional) An object containing properties which may override the options
13854      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13855      * the most recently used options are reused).
13856      */
13857     reload : function(options){
13858         this.load(Roo.applyIf(options||{}, this.lastOptions));
13859     },
13860
13861     // private
13862     // Called as a callback by the Reader during a load operation.
13863     loadRecords : function(o, options, success){
13864         if(!o || success === false){
13865             if(success !== false){
13866                 this.fireEvent("load", this, [], options, o);
13867             }
13868             if(options.callback){
13869                 options.callback.call(options.scope || this, [], options, false);
13870             }
13871             return;
13872         }
13873         // if data returned failure - throw an exception.
13874         if (o.success === false) {
13875             // show a message if no listener is registered.
13876             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13877                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13878             }
13879             // loadmask wil be hooked into this..
13880             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13881             return;
13882         }
13883         var r = o.records, t = o.totalRecords || r.length;
13884         
13885         this.fireEvent("beforeloadadd", this, r, options, o);
13886         
13887         if(!options || options.add !== true){
13888             if(this.pruneModifiedRecords){
13889                 this.modified = [];
13890             }
13891             for(var i = 0, len = r.length; i < len; i++){
13892                 r[i].join(this);
13893             }
13894             if(this.snapshot){
13895                 this.data = this.snapshot;
13896                 delete this.snapshot;
13897             }
13898             this.data.clear();
13899             this.data.addAll(r);
13900             this.totalLength = t;
13901             this.applySort();
13902             this.fireEvent("datachanged", this);
13903         }else{
13904             this.totalLength = Math.max(t, this.data.length+r.length);
13905             this.add(r);
13906         }
13907         
13908         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13909                 
13910             var e = new Roo.data.Record({});
13911
13912             e.set(this.parent.displayField, this.parent.emptyTitle);
13913             e.set(this.parent.valueField, '');
13914
13915             this.insert(0, e);
13916         }
13917             
13918         this.fireEvent("load", this, r, options, o);
13919         if(options.callback){
13920             options.callback.call(options.scope || this, r, options, true);
13921         }
13922     },
13923
13924
13925     /**
13926      * Loads data from a passed data block. A Reader which understands the format of the data
13927      * must have been configured in the constructor.
13928      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13929      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13930      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13931      */
13932     loadData : function(o, append){
13933         var r = this.reader.readRecords(o);
13934         this.loadRecords(r, {add: append}, true);
13935     },
13936     
13937      /**
13938      * using 'cn' the nested child reader read the child array into it's child stores.
13939      * @param {Object} rec The record with a 'children array
13940      */
13941     loadDataFromChildren : function(rec)
13942     {
13943         this.loadData(this.reader.toLoadData(rec));
13944     },
13945     
13946
13947     /**
13948      * Gets the number of cached records.
13949      * <p>
13950      * <em>If using paging, this may not be the total size of the dataset. If the data object
13951      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13952      * the data set size</em>
13953      */
13954     getCount : function(){
13955         return this.data.length || 0;
13956     },
13957
13958     /**
13959      * Gets the total number of records in the dataset as returned by the server.
13960      * <p>
13961      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13962      * the dataset size</em>
13963      */
13964     getTotalCount : function(){
13965         return this.totalLength || 0;
13966     },
13967
13968     /**
13969      * Returns the sort state of the Store as an object with two properties:
13970      * <pre><code>
13971  field {String} The name of the field by which the Records are sorted
13972  direction {String} The sort order, "ASC" or "DESC"
13973      * </code></pre>
13974      */
13975     getSortState : function(){
13976         return this.sortInfo;
13977     },
13978
13979     // private
13980     applySort : function(){
13981         if(this.sortInfo && !this.remoteSort){
13982             var s = this.sortInfo, f = s.field;
13983             var st = this.fields.get(f).sortType;
13984             var fn = function(r1, r2){
13985                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13986                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13987             };
13988             this.data.sort(s.direction, fn);
13989             if(this.snapshot && this.snapshot != this.data){
13990                 this.snapshot.sort(s.direction, fn);
13991             }
13992         }
13993     },
13994
13995     /**
13996      * Sets the default sort column and order to be used by the next load operation.
13997      * @param {String} fieldName The name of the field to sort by.
13998      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13999      */
14000     setDefaultSort : function(field, dir){
14001         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14002     },
14003
14004     /**
14005      * Sort the Records.
14006      * If remote sorting is used, the sort is performed on the server, and the cache is
14007      * reloaded. If local sorting is used, the cache is sorted internally.
14008      * @param {String} fieldName The name of the field to sort by.
14009      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14010      */
14011     sort : function(fieldName, dir){
14012         var f = this.fields.get(fieldName);
14013         if(!dir){
14014             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14015             
14016             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14017                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14018             }else{
14019                 dir = f.sortDir;
14020             }
14021         }
14022         this.sortToggle[f.name] = dir;
14023         this.sortInfo = {field: f.name, direction: dir};
14024         if(!this.remoteSort){
14025             this.applySort();
14026             this.fireEvent("datachanged", this);
14027         }else{
14028             this.load(this.lastOptions);
14029         }
14030     },
14031
14032     /**
14033      * Calls the specified function for each of the Records in the cache.
14034      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14035      * Returning <em>false</em> aborts and exits the iteration.
14036      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14037      */
14038     each : function(fn, scope){
14039         this.data.each(fn, scope);
14040     },
14041
14042     /**
14043      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14044      * (e.g., during paging).
14045      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14046      */
14047     getModifiedRecords : function(){
14048         return this.modified;
14049     },
14050
14051     // private
14052     createFilterFn : function(property, value, anyMatch){
14053         if(!value.exec){ // not a regex
14054             value = String(value);
14055             if(value.length == 0){
14056                 return false;
14057             }
14058             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14059         }
14060         return function(r){
14061             return value.test(r.data[property]);
14062         };
14063     },
14064
14065     /**
14066      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14067      * @param {String} property A field on your records
14068      * @param {Number} start The record index to start at (defaults to 0)
14069      * @param {Number} end The last record index to include (defaults to length - 1)
14070      * @return {Number} The sum
14071      */
14072     sum : function(property, start, end){
14073         var rs = this.data.items, v = 0;
14074         start = start || 0;
14075         end = (end || end === 0) ? end : rs.length-1;
14076
14077         for(var i = start; i <= end; i++){
14078             v += (rs[i].data[property] || 0);
14079         }
14080         return v;
14081     },
14082
14083     /**
14084      * Filter the records by a specified property.
14085      * @param {String} field A field on your records
14086      * @param {String/RegExp} value Either a string that the field
14087      * should start with or a RegExp to test against the field
14088      * @param {Boolean} anyMatch True to match any part not just the beginning
14089      */
14090     filter : function(property, value, anyMatch){
14091         var fn = this.createFilterFn(property, value, anyMatch);
14092         return fn ? this.filterBy(fn) : this.clearFilter();
14093     },
14094
14095     /**
14096      * Filter by a function. The specified function will be called with each
14097      * record in this data source. If the function returns true the record is included,
14098      * otherwise it is filtered.
14099      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14100      * @param {Object} scope (optional) The scope of the function (defaults to this)
14101      */
14102     filterBy : function(fn, scope){
14103         this.snapshot = this.snapshot || this.data;
14104         this.data = this.queryBy(fn, scope||this);
14105         this.fireEvent("datachanged", this);
14106     },
14107
14108     /**
14109      * Query the records by a specified property.
14110      * @param {String} field A field on your records
14111      * @param {String/RegExp} value Either a string that the field
14112      * should start with or a RegExp to test against the field
14113      * @param {Boolean} anyMatch True to match any part not just the beginning
14114      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14115      */
14116     query : function(property, value, anyMatch){
14117         var fn = this.createFilterFn(property, value, anyMatch);
14118         return fn ? this.queryBy(fn) : this.data.clone();
14119     },
14120
14121     /**
14122      * Query by a function. The specified function will be called with each
14123      * record in this data source. If the function returns true the record is included
14124      * in the results.
14125      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14126      * @param {Object} scope (optional) The scope of the function (defaults to this)
14127       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14128      **/
14129     queryBy : function(fn, scope){
14130         var data = this.snapshot || this.data;
14131         return data.filterBy(fn, scope||this);
14132     },
14133
14134     /**
14135      * Collects unique values for a particular dataIndex from this store.
14136      * @param {String} dataIndex The property to collect
14137      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14138      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14139      * @return {Array} An array of the unique values
14140      **/
14141     collect : function(dataIndex, allowNull, bypassFilter){
14142         var d = (bypassFilter === true && this.snapshot) ?
14143                 this.snapshot.items : this.data.items;
14144         var v, sv, r = [], l = {};
14145         for(var i = 0, len = d.length; i < len; i++){
14146             v = d[i].data[dataIndex];
14147             sv = String(v);
14148             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14149                 l[sv] = true;
14150                 r[r.length] = v;
14151             }
14152         }
14153         return r;
14154     },
14155
14156     /**
14157      * Revert to a view of the Record cache with no filtering applied.
14158      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14159      */
14160     clearFilter : function(suppressEvent){
14161         if(this.snapshot && this.snapshot != this.data){
14162             this.data = this.snapshot;
14163             delete this.snapshot;
14164             if(suppressEvent !== true){
14165                 this.fireEvent("datachanged", this);
14166             }
14167         }
14168     },
14169
14170     // private
14171     afterEdit : function(record){
14172         if(this.modified.indexOf(record) == -1){
14173             this.modified.push(record);
14174         }
14175         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14176     },
14177     
14178     // private
14179     afterReject : function(record){
14180         this.modified.remove(record);
14181         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14182     },
14183
14184     // private
14185     afterCommit : function(record){
14186         this.modified.remove(record);
14187         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14188     },
14189
14190     /**
14191      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14192      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14193      */
14194     commitChanges : function(){
14195         var m = this.modified.slice(0);
14196         this.modified = [];
14197         for(var i = 0, len = m.length; i < len; i++){
14198             m[i].commit();
14199         }
14200     },
14201
14202     /**
14203      * Cancel outstanding changes on all changed records.
14204      */
14205     rejectChanges : function(){
14206         var m = this.modified.slice(0);
14207         this.modified = [];
14208         for(var i = 0, len = m.length; i < len; i++){
14209             m[i].reject();
14210         }
14211     },
14212
14213     onMetaChange : function(meta, rtype, o){
14214         this.recordType = rtype;
14215         this.fields = rtype.prototype.fields;
14216         delete this.snapshot;
14217         this.sortInfo = meta.sortInfo || this.sortInfo;
14218         this.modified = [];
14219         this.fireEvent('metachange', this, this.reader.meta);
14220     },
14221     
14222     moveIndex : function(data, type)
14223     {
14224         var index = this.indexOf(data);
14225         
14226         var newIndex = index + type;
14227         
14228         this.remove(data);
14229         
14230         this.insert(newIndex, data);
14231         
14232     }
14233 });/*
14234  * Based on:
14235  * Ext JS Library 1.1.1
14236  * Copyright(c) 2006-2007, Ext JS, LLC.
14237  *
14238  * Originally Released Under LGPL - original licence link has changed is not relivant.
14239  *
14240  * Fork - LGPL
14241  * <script type="text/javascript">
14242  */
14243
14244 /**
14245  * @class Roo.data.SimpleStore
14246  * @extends Roo.data.Store
14247  * Small helper class to make creating Stores from Array data easier.
14248  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14249  * @cfg {Array} fields An array of field definition objects, or field name strings.
14250  * @cfg {Object} an existing reader (eg. copied from another store)
14251  * @cfg {Array} data The multi-dimensional array of data
14252  * @constructor
14253  * @param {Object} config
14254  */
14255 Roo.data.SimpleStore = function(config)
14256 {
14257     Roo.data.SimpleStore.superclass.constructor.call(this, {
14258         isLocal : true,
14259         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14260                 id: config.id
14261             },
14262             Roo.data.Record.create(config.fields)
14263         ),
14264         proxy : new Roo.data.MemoryProxy(config.data)
14265     });
14266     this.load();
14267 };
14268 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14269  * Based on:
14270  * Ext JS Library 1.1.1
14271  * Copyright(c) 2006-2007, Ext JS, LLC.
14272  *
14273  * Originally Released Under LGPL - original licence link has changed is not relivant.
14274  *
14275  * Fork - LGPL
14276  * <script type="text/javascript">
14277  */
14278
14279 /**
14280 /**
14281  * @extends Roo.data.Store
14282  * @class Roo.data.JsonStore
14283  * Small helper class to make creating Stores for JSON data easier. <br/>
14284 <pre><code>
14285 var store = new Roo.data.JsonStore({
14286     url: 'get-images.php',
14287     root: 'images',
14288     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14289 });
14290 </code></pre>
14291  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14292  * JsonReader and HttpProxy (unless inline data is provided).</b>
14293  * @cfg {Array} fields An array of field definition objects, or field name strings.
14294  * @constructor
14295  * @param {Object} config
14296  */
14297 Roo.data.JsonStore = function(c){
14298     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14299         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14300         reader: new Roo.data.JsonReader(c, c.fields)
14301     }));
14302 };
14303 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14304  * Based on:
14305  * Ext JS Library 1.1.1
14306  * Copyright(c) 2006-2007, Ext JS, LLC.
14307  *
14308  * Originally Released Under LGPL - original licence link has changed is not relivant.
14309  *
14310  * Fork - LGPL
14311  * <script type="text/javascript">
14312  */
14313
14314  
14315 Roo.data.Field = function(config){
14316     if(typeof config == "string"){
14317         config = {name: config};
14318     }
14319     Roo.apply(this, config);
14320     
14321     if(!this.type){
14322         this.type = "auto";
14323     }
14324     
14325     var st = Roo.data.SortTypes;
14326     // named sortTypes are supported, here we look them up
14327     if(typeof this.sortType == "string"){
14328         this.sortType = st[this.sortType];
14329     }
14330     
14331     // set default sortType for strings and dates
14332     if(!this.sortType){
14333         switch(this.type){
14334             case "string":
14335                 this.sortType = st.asUCString;
14336                 break;
14337             case "date":
14338                 this.sortType = st.asDate;
14339                 break;
14340             default:
14341                 this.sortType = st.none;
14342         }
14343     }
14344
14345     // define once
14346     var stripRe = /[\$,%]/g;
14347
14348     // prebuilt conversion function for this field, instead of
14349     // switching every time we're reading a value
14350     if(!this.convert){
14351         var cv, dateFormat = this.dateFormat;
14352         switch(this.type){
14353             case "":
14354             case "auto":
14355             case undefined:
14356                 cv = function(v){ return v; };
14357                 break;
14358             case "string":
14359                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14360                 break;
14361             case "int":
14362                 cv = function(v){
14363                     return v !== undefined && v !== null && v !== '' ?
14364                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14365                     };
14366                 break;
14367             case "float":
14368                 cv = function(v){
14369                     return v !== undefined && v !== null && v !== '' ?
14370                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14371                     };
14372                 break;
14373             case "bool":
14374             case "boolean":
14375                 cv = function(v){ return v === true || v === "true" || v == 1; };
14376                 break;
14377             case "date":
14378                 cv = function(v){
14379                     if(!v){
14380                         return '';
14381                     }
14382                     if(v instanceof Date){
14383                         return v;
14384                     }
14385                     if(dateFormat){
14386                         if(dateFormat == "timestamp"){
14387                             return new Date(v*1000);
14388                         }
14389                         return Date.parseDate(v, dateFormat);
14390                     }
14391                     var parsed = Date.parse(v);
14392                     return parsed ? new Date(parsed) : null;
14393                 };
14394              break;
14395             
14396         }
14397         this.convert = cv;
14398     }
14399 };
14400
14401 Roo.data.Field.prototype = {
14402     dateFormat: null,
14403     defaultValue: "",
14404     mapping: null,
14405     sortType : null,
14406     sortDir : "ASC"
14407 };/*
14408  * Based on:
14409  * Ext JS Library 1.1.1
14410  * Copyright(c) 2006-2007, Ext JS, LLC.
14411  *
14412  * Originally Released Under LGPL - original licence link has changed is not relivant.
14413  *
14414  * Fork - LGPL
14415  * <script type="text/javascript">
14416  */
14417  
14418 // Base class for reading structured data from a data source.  This class is intended to be
14419 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14420
14421 /**
14422  * @class Roo.data.DataReader
14423  * Base class for reading structured data from a data source.  This class is intended to be
14424  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14425  */
14426
14427 Roo.data.DataReader = function(meta, recordType){
14428     
14429     this.meta = meta;
14430     
14431     this.recordType = recordType instanceof Array ? 
14432         Roo.data.Record.create(recordType) : recordType;
14433 };
14434
14435 Roo.data.DataReader.prototype = {
14436     
14437     
14438     readerType : 'Data',
14439      /**
14440      * Create an empty record
14441      * @param {Object} data (optional) - overlay some values
14442      * @return {Roo.data.Record} record created.
14443      */
14444     newRow :  function(d) {
14445         var da =  {};
14446         this.recordType.prototype.fields.each(function(c) {
14447             switch( c.type) {
14448                 case 'int' : da[c.name] = 0; break;
14449                 case 'date' : da[c.name] = new Date(); break;
14450                 case 'float' : da[c.name] = 0.0; break;
14451                 case 'boolean' : da[c.name] = false; break;
14452                 default : da[c.name] = ""; break;
14453             }
14454             
14455         });
14456         return new this.recordType(Roo.apply(da, d));
14457     }
14458     
14459     
14460 };/*
14461  * Based on:
14462  * Ext JS Library 1.1.1
14463  * Copyright(c) 2006-2007, Ext JS, LLC.
14464  *
14465  * Originally Released Under LGPL - original licence link has changed is not relivant.
14466  *
14467  * Fork - LGPL
14468  * <script type="text/javascript">
14469  */
14470
14471 /**
14472  * @class Roo.data.DataProxy
14473  * @extends Roo.data.Observable
14474  * This class is an abstract base class for implementations which provide retrieval of
14475  * unformatted data objects.<br>
14476  * <p>
14477  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14478  * (of the appropriate type which knows how to parse the data object) to provide a block of
14479  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14480  * <p>
14481  * Custom implementations must implement the load method as described in
14482  * {@link Roo.data.HttpProxy#load}.
14483  */
14484 Roo.data.DataProxy = function(){
14485     this.addEvents({
14486         /**
14487          * @event beforeload
14488          * Fires before a network request is made to retrieve a data object.
14489          * @param {Object} This DataProxy object.
14490          * @param {Object} params The params parameter to the load function.
14491          */
14492         beforeload : true,
14493         /**
14494          * @event load
14495          * Fires before the load method's callback is called.
14496          * @param {Object} This DataProxy object.
14497          * @param {Object} o The data object.
14498          * @param {Object} arg The callback argument object passed to the load function.
14499          */
14500         load : true,
14501         /**
14502          * @event loadexception
14503          * Fires if an Exception occurs during data retrieval.
14504          * @param {Object} This DataProxy object.
14505          * @param {Object} o The data object.
14506          * @param {Object} arg The callback argument object passed to the load function.
14507          * @param {Object} e The Exception.
14508          */
14509         loadexception : true
14510     });
14511     Roo.data.DataProxy.superclass.constructor.call(this);
14512 };
14513
14514 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14515
14516     /**
14517      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14518      */
14519 /*
14520  * Based on:
14521  * Ext JS Library 1.1.1
14522  * Copyright(c) 2006-2007, Ext JS, LLC.
14523  *
14524  * Originally Released Under LGPL - original licence link has changed is not relivant.
14525  *
14526  * Fork - LGPL
14527  * <script type="text/javascript">
14528  */
14529 /**
14530  * @class Roo.data.MemoryProxy
14531  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14532  * to the Reader when its load method is called.
14533  * @constructor
14534  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14535  */
14536 Roo.data.MemoryProxy = function(data){
14537     if (data.data) {
14538         data = data.data;
14539     }
14540     Roo.data.MemoryProxy.superclass.constructor.call(this);
14541     this.data = data;
14542 };
14543
14544 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14545     
14546     /**
14547      * Load data from the requested source (in this case an in-memory
14548      * data object passed to the constructor), read the data object into
14549      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14550      * process that block using the passed callback.
14551      * @param {Object} params This parameter is not used by the MemoryProxy class.
14552      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14553      * object into a block of Roo.data.Records.
14554      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14555      * The function must be passed <ul>
14556      * <li>The Record block object</li>
14557      * <li>The "arg" argument from the load function</li>
14558      * <li>A boolean success indicator</li>
14559      * </ul>
14560      * @param {Object} scope The scope in which to call the callback
14561      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14562      */
14563     load : function(params, reader, callback, scope, arg){
14564         params = params || {};
14565         var result;
14566         try {
14567             result = reader.readRecords(params.data ? params.data :this.data);
14568         }catch(e){
14569             this.fireEvent("loadexception", this, arg, null, e);
14570             callback.call(scope, null, arg, false);
14571             return;
14572         }
14573         callback.call(scope, result, arg, true);
14574     },
14575     
14576     // private
14577     update : function(params, records){
14578         
14579     }
14580 });/*
14581  * Based on:
14582  * Ext JS Library 1.1.1
14583  * Copyright(c) 2006-2007, Ext JS, LLC.
14584  *
14585  * Originally Released Under LGPL - original licence link has changed is not relivant.
14586  *
14587  * Fork - LGPL
14588  * <script type="text/javascript">
14589  */
14590 /**
14591  * @class Roo.data.HttpProxy
14592  * @extends Roo.data.DataProxy
14593  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14594  * configured to reference a certain URL.<br><br>
14595  * <p>
14596  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14597  * from which the running page was served.<br><br>
14598  * <p>
14599  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14600  * <p>
14601  * Be aware that to enable the browser to parse an XML document, the server must set
14602  * the Content-Type header in the HTTP response to "text/xml".
14603  * @constructor
14604  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14605  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14606  * will be used to make the request.
14607  */
14608 Roo.data.HttpProxy = function(conn){
14609     Roo.data.HttpProxy.superclass.constructor.call(this);
14610     // is conn a conn config or a real conn?
14611     this.conn = conn;
14612     this.useAjax = !conn || !conn.events;
14613   
14614 };
14615
14616 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14617     // thse are take from connection...
14618     
14619     /**
14620      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14621      */
14622     /**
14623      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14624      * extra parameters to each request made by this object. (defaults to undefined)
14625      */
14626     /**
14627      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14628      *  to each request made by this object. (defaults to undefined)
14629      */
14630     /**
14631      * @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)
14632      */
14633     /**
14634      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14635      */
14636      /**
14637      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14638      * @type Boolean
14639      */
14640   
14641
14642     /**
14643      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14644      * @type Boolean
14645      */
14646     /**
14647      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14648      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14649      * a finer-grained basis than the DataProxy events.
14650      */
14651     getConnection : function(){
14652         return this.useAjax ? Roo.Ajax : this.conn;
14653     },
14654
14655     /**
14656      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14657      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14658      * process that block using the passed callback.
14659      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14660      * for the request to the remote server.
14661      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14662      * object into a block of Roo.data.Records.
14663      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14664      * The function must be passed <ul>
14665      * <li>The Record block object</li>
14666      * <li>The "arg" argument from the load function</li>
14667      * <li>A boolean success indicator</li>
14668      * </ul>
14669      * @param {Object} scope The scope in which to call the callback
14670      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14671      */
14672     load : function(params, reader, callback, scope, arg){
14673         if(this.fireEvent("beforeload", this, params) !== false){
14674             var  o = {
14675                 params : params || {},
14676                 request: {
14677                     callback : callback,
14678                     scope : scope,
14679                     arg : arg
14680                 },
14681                 reader: reader,
14682                 callback : this.loadResponse,
14683                 scope: this
14684             };
14685             if(this.useAjax){
14686                 Roo.applyIf(o, this.conn);
14687                 if(this.activeRequest){
14688                     Roo.Ajax.abort(this.activeRequest);
14689                 }
14690                 this.activeRequest = Roo.Ajax.request(o);
14691             }else{
14692                 this.conn.request(o);
14693             }
14694         }else{
14695             callback.call(scope||this, null, arg, false);
14696         }
14697     },
14698
14699     // private
14700     loadResponse : function(o, success, response){
14701         delete this.activeRequest;
14702         if(!success){
14703             this.fireEvent("loadexception", this, o, response);
14704             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14705             return;
14706         }
14707         var result;
14708         try {
14709             result = o.reader.read(response);
14710         }catch(e){
14711             this.fireEvent("loadexception", this, o, response, e);
14712             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14713             return;
14714         }
14715         
14716         this.fireEvent("load", this, o, o.request.arg);
14717         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14718     },
14719
14720     // private
14721     update : function(dataSet){
14722
14723     },
14724
14725     // private
14726     updateResponse : function(dataSet){
14727
14728     }
14729 });/*
14730  * Based on:
14731  * Ext JS Library 1.1.1
14732  * Copyright(c) 2006-2007, Ext JS, LLC.
14733  *
14734  * Originally Released Under LGPL - original licence link has changed is not relivant.
14735  *
14736  * Fork - LGPL
14737  * <script type="text/javascript">
14738  */
14739
14740 /**
14741  * @class Roo.data.ScriptTagProxy
14742  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14743  * other than the originating domain of the running page.<br><br>
14744  * <p>
14745  * <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
14746  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14747  * <p>
14748  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14749  * source code that is used as the source inside a &lt;script> tag.<br><br>
14750  * <p>
14751  * In order for the browser to process the returned data, the server must wrap the data object
14752  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14753  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14754  * depending on whether the callback name was passed:
14755  * <p>
14756  * <pre><code>
14757 boolean scriptTag = false;
14758 String cb = request.getParameter("callback");
14759 if (cb != null) {
14760     scriptTag = true;
14761     response.setContentType("text/javascript");
14762 } else {
14763     response.setContentType("application/x-json");
14764 }
14765 Writer out = response.getWriter();
14766 if (scriptTag) {
14767     out.write(cb + "(");
14768 }
14769 out.print(dataBlock.toJsonString());
14770 if (scriptTag) {
14771     out.write(");");
14772 }
14773 </pre></code>
14774  *
14775  * @constructor
14776  * @param {Object} config A configuration object.
14777  */
14778 Roo.data.ScriptTagProxy = function(config){
14779     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14780     Roo.apply(this, config);
14781     this.head = document.getElementsByTagName("head")[0];
14782 };
14783
14784 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14785
14786 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14787     /**
14788      * @cfg {String} url The URL from which to request the data object.
14789      */
14790     /**
14791      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14792      */
14793     timeout : 30000,
14794     /**
14795      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14796      * the server the name of the callback function set up by the load call to process the returned data object.
14797      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14798      * javascript output which calls this named function passing the data object as its only parameter.
14799      */
14800     callbackParam : "callback",
14801     /**
14802      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14803      * name to the request.
14804      */
14805     nocache : true,
14806
14807     /**
14808      * Load data from the configured URL, read the data object into
14809      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14810      * process that block using the passed callback.
14811      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14812      * for the request to the remote server.
14813      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14814      * object into a block of Roo.data.Records.
14815      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14816      * The function must be passed <ul>
14817      * <li>The Record block object</li>
14818      * <li>The "arg" argument from the load function</li>
14819      * <li>A boolean success indicator</li>
14820      * </ul>
14821      * @param {Object} scope The scope in which to call the callback
14822      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14823      */
14824     load : function(params, reader, callback, scope, arg){
14825         if(this.fireEvent("beforeload", this, params) !== false){
14826
14827             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14828
14829             var url = this.url;
14830             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14831             if(this.nocache){
14832                 url += "&_dc=" + (new Date().getTime());
14833             }
14834             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14835             var trans = {
14836                 id : transId,
14837                 cb : "stcCallback"+transId,
14838                 scriptId : "stcScript"+transId,
14839                 params : params,
14840                 arg : arg,
14841                 url : url,
14842                 callback : callback,
14843                 scope : scope,
14844                 reader : reader
14845             };
14846             var conn = this;
14847
14848             window[trans.cb] = function(o){
14849                 conn.handleResponse(o, trans);
14850             };
14851
14852             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14853
14854             if(this.autoAbort !== false){
14855                 this.abort();
14856             }
14857
14858             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14859
14860             var script = document.createElement("script");
14861             script.setAttribute("src", url);
14862             script.setAttribute("type", "text/javascript");
14863             script.setAttribute("id", trans.scriptId);
14864             this.head.appendChild(script);
14865
14866             this.trans = trans;
14867         }else{
14868             callback.call(scope||this, null, arg, false);
14869         }
14870     },
14871
14872     // private
14873     isLoading : function(){
14874         return this.trans ? true : false;
14875     },
14876
14877     /**
14878      * Abort the current server request.
14879      */
14880     abort : function(){
14881         if(this.isLoading()){
14882             this.destroyTrans(this.trans);
14883         }
14884     },
14885
14886     // private
14887     destroyTrans : function(trans, isLoaded){
14888         this.head.removeChild(document.getElementById(trans.scriptId));
14889         clearTimeout(trans.timeoutId);
14890         if(isLoaded){
14891             window[trans.cb] = undefined;
14892             try{
14893                 delete window[trans.cb];
14894             }catch(e){}
14895         }else{
14896             // if hasn't been loaded, wait for load to remove it to prevent script error
14897             window[trans.cb] = function(){
14898                 window[trans.cb] = undefined;
14899                 try{
14900                     delete window[trans.cb];
14901                 }catch(e){}
14902             };
14903         }
14904     },
14905
14906     // private
14907     handleResponse : function(o, trans){
14908         this.trans = false;
14909         this.destroyTrans(trans, true);
14910         var result;
14911         try {
14912             result = trans.reader.readRecords(o);
14913         }catch(e){
14914             this.fireEvent("loadexception", this, o, trans.arg, e);
14915             trans.callback.call(trans.scope||window, null, trans.arg, false);
14916             return;
14917         }
14918         this.fireEvent("load", this, o, trans.arg);
14919         trans.callback.call(trans.scope||window, result, trans.arg, true);
14920     },
14921
14922     // private
14923     handleFailure : function(trans){
14924         this.trans = false;
14925         this.destroyTrans(trans, false);
14926         this.fireEvent("loadexception", this, null, trans.arg);
14927         trans.callback.call(trans.scope||window, null, trans.arg, false);
14928     }
14929 });/*
14930  * Based on:
14931  * Ext JS Library 1.1.1
14932  * Copyright(c) 2006-2007, Ext JS, LLC.
14933  *
14934  * Originally Released Under LGPL - original licence link has changed is not relivant.
14935  *
14936  * Fork - LGPL
14937  * <script type="text/javascript">
14938  */
14939
14940 /**
14941  * @class Roo.data.JsonReader
14942  * @extends Roo.data.DataReader
14943  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14944  * based on mappings in a provided Roo.data.Record constructor.
14945  * 
14946  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14947  * in the reply previously. 
14948  * 
14949  * <p>
14950  * Example code:
14951  * <pre><code>
14952 var RecordDef = Roo.data.Record.create([
14953     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14954     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14955 ]);
14956 var myReader = new Roo.data.JsonReader({
14957     totalProperty: "results",    // The property which contains the total dataset size (optional)
14958     root: "rows",                // The property which contains an Array of row objects
14959     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14960 }, RecordDef);
14961 </code></pre>
14962  * <p>
14963  * This would consume a JSON file like this:
14964  * <pre><code>
14965 { 'results': 2, 'rows': [
14966     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14967     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14968 }
14969 </code></pre>
14970  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14971  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14972  * paged from the remote server.
14973  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14974  * @cfg {String} root name of the property which contains the Array of row objects.
14975  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14976  * @cfg {Array} fields Array of field definition objects
14977  * @constructor
14978  * Create a new JsonReader
14979  * @param {Object} meta Metadata configuration options
14980  * @param {Object} recordType Either an Array of field definition objects,
14981  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14982  */
14983 Roo.data.JsonReader = function(meta, recordType){
14984     
14985     meta = meta || {};
14986     // set some defaults:
14987     Roo.applyIf(meta, {
14988         totalProperty: 'total',
14989         successProperty : 'success',
14990         root : 'data',
14991         id : 'id'
14992     });
14993     
14994     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14995 };
14996 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14997     
14998     readerType : 'Json',
14999     
15000     /**
15001      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15002      * Used by Store query builder to append _requestMeta to params.
15003      * 
15004      */
15005     metaFromRemote : false,
15006     /**
15007      * This method is only used by a DataProxy which has retrieved data from a remote server.
15008      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15009      * @return {Object} data A data block which is used by an Roo.data.Store object as
15010      * a cache of Roo.data.Records.
15011      */
15012     read : function(response){
15013         var json = response.responseText;
15014        
15015         var o = /* eval:var:o */ eval("("+json+")");
15016         if(!o) {
15017             throw {message: "JsonReader.read: Json object not found"};
15018         }
15019         
15020         if(o.metaData){
15021             
15022             delete this.ef;
15023             this.metaFromRemote = true;
15024             this.meta = o.metaData;
15025             this.recordType = Roo.data.Record.create(o.metaData.fields);
15026             this.onMetaChange(this.meta, this.recordType, o);
15027         }
15028         return this.readRecords(o);
15029     },
15030
15031     // private function a store will implement
15032     onMetaChange : function(meta, recordType, o){
15033
15034     },
15035
15036     /**
15037          * @ignore
15038          */
15039     simpleAccess: function(obj, subsc) {
15040         return obj[subsc];
15041     },
15042
15043         /**
15044          * @ignore
15045          */
15046     getJsonAccessor: function(){
15047         var re = /[\[\.]/;
15048         return function(expr) {
15049             try {
15050                 return(re.test(expr))
15051                     ? new Function("obj", "return obj." + expr)
15052                     : function(obj){
15053                         return obj[expr];
15054                     };
15055             } catch(e){}
15056             return Roo.emptyFn;
15057         };
15058     }(),
15059
15060     /**
15061      * Create a data block containing Roo.data.Records from an XML document.
15062      * @param {Object} o An object which contains an Array of row objects in the property specified
15063      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15064      * which contains the total size of the dataset.
15065      * @return {Object} data A data block which is used by an Roo.data.Store object as
15066      * a cache of Roo.data.Records.
15067      */
15068     readRecords : function(o){
15069         /**
15070          * After any data loads, the raw JSON data is available for further custom processing.
15071          * @type Object
15072          */
15073         this.o = o;
15074         var s = this.meta, Record = this.recordType,
15075             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15076
15077 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15078         if (!this.ef) {
15079             if(s.totalProperty) {
15080                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15081                 }
15082                 if(s.successProperty) {
15083                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15084                 }
15085                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15086                 if (s.id) {
15087                         var g = this.getJsonAccessor(s.id);
15088                         this.getId = function(rec) {
15089                                 var r = g(rec);  
15090                                 return (r === undefined || r === "") ? null : r;
15091                         };
15092                 } else {
15093                         this.getId = function(){return null;};
15094                 }
15095             this.ef = [];
15096             for(var jj = 0; jj < fl; jj++){
15097                 f = fi[jj];
15098                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15099                 this.ef[jj] = this.getJsonAccessor(map);
15100             }
15101         }
15102
15103         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15104         if(s.totalProperty){
15105             var vt = parseInt(this.getTotal(o), 10);
15106             if(!isNaN(vt)){
15107                 totalRecords = vt;
15108             }
15109         }
15110         if(s.successProperty){
15111             var vs = this.getSuccess(o);
15112             if(vs === false || vs === 'false'){
15113                 success = false;
15114             }
15115         }
15116         var records = [];
15117         for(var i = 0; i < c; i++){
15118                 var n = root[i];
15119             var values = {};
15120             var id = this.getId(n);
15121             for(var j = 0; j < fl; j++){
15122                 f = fi[j];
15123             var v = this.ef[j](n);
15124             if (!f.convert) {
15125                 Roo.log('missing convert for ' + f.name);
15126                 Roo.log(f);
15127                 continue;
15128             }
15129             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15130             }
15131             var record = new Record(values, id);
15132             record.json = n;
15133             records[i] = record;
15134         }
15135         return {
15136             raw : o,
15137             success : success,
15138             records : records,
15139             totalRecords : totalRecords
15140         };
15141     },
15142     // used when loading children.. @see loadDataFromChildren
15143     toLoadData: function(rec)
15144     {
15145         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15146         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15147         return { data : data, total : data.length };
15148         
15149     }
15150 });/*
15151  * Based on:
15152  * Ext JS Library 1.1.1
15153  * Copyright(c) 2006-2007, Ext JS, LLC.
15154  *
15155  * Originally Released Under LGPL - original licence link has changed is not relivant.
15156  *
15157  * Fork - LGPL
15158  * <script type="text/javascript">
15159  */
15160
15161 /**
15162  * @class Roo.data.ArrayReader
15163  * @extends Roo.data.DataReader
15164  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15165  * Each element of that Array represents a row of data fields. The
15166  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15167  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15168  * <p>
15169  * Example code:.
15170  * <pre><code>
15171 var RecordDef = Roo.data.Record.create([
15172     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15173     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15174 ]);
15175 var myReader = new Roo.data.ArrayReader({
15176     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15177 }, RecordDef);
15178 </code></pre>
15179  * <p>
15180  * This would consume an Array like this:
15181  * <pre><code>
15182 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15183   </code></pre>
15184  
15185  * @constructor
15186  * Create a new JsonReader
15187  * @param {Object} meta Metadata configuration options.
15188  * @param {Object|Array} recordType Either an Array of field definition objects
15189  * 
15190  * @cfg {Array} fields Array of field definition objects
15191  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15192  * as specified to {@link Roo.data.Record#create},
15193  * or an {@link Roo.data.Record} object
15194  *
15195  * 
15196  * created using {@link Roo.data.Record#create}.
15197  */
15198 Roo.data.ArrayReader = function(meta, recordType)
15199 {    
15200     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15201 };
15202
15203 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15204     
15205       /**
15206      * Create a data block containing Roo.data.Records from an XML document.
15207      * @param {Object} o An Array of row objects which represents the dataset.
15208      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15209      * a cache of Roo.data.Records.
15210      */
15211     readRecords : function(o)
15212     {
15213         var sid = this.meta ? this.meta.id : null;
15214         var recordType = this.recordType, fields = recordType.prototype.fields;
15215         var records = [];
15216         var root = o;
15217         for(var i = 0; i < root.length; i++){
15218             var n = root[i];
15219             var values = {};
15220             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15221             for(var j = 0, jlen = fields.length; j < jlen; j++){
15222                 var f = fields.items[j];
15223                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15224                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15225                 v = f.convert(v);
15226                 values[f.name] = v;
15227             }
15228             var record = new recordType(values, id);
15229             record.json = n;
15230             records[records.length] = record;
15231         }
15232         return {
15233             records : records,
15234             totalRecords : records.length
15235         };
15236     },
15237     // used when loading children.. @see loadDataFromChildren
15238     toLoadData: function(rec)
15239     {
15240         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15241         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15242         
15243     }
15244     
15245     
15246 });/*
15247  * - LGPL
15248  * * 
15249  */
15250
15251 /**
15252  * @class Roo.bootstrap.ComboBox
15253  * @extends Roo.bootstrap.TriggerField
15254  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15255  * @cfg {Boolean} append (true|false) default false
15256  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15257  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15258  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15259  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15260  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15261  * @cfg {Boolean} animate default true
15262  * @cfg {Boolean} emptyResultText only for touch device
15263  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15264  * @cfg {String} emptyTitle default ''
15265  * @cfg {Number} width fixed with? experimental
15266  * @constructor
15267  * Create a new ComboBox.
15268  * @param {Object} config Configuration options
15269  */
15270 Roo.bootstrap.ComboBox = function(config){
15271     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15272     this.addEvents({
15273         /**
15274          * @event expand
15275          * Fires when the dropdown list is expanded
15276         * @param {Roo.bootstrap.ComboBox} combo This combo box
15277         */
15278         'expand' : true,
15279         /**
15280          * @event collapse
15281          * Fires when the dropdown list is collapsed
15282         * @param {Roo.bootstrap.ComboBox} combo This combo box
15283         */
15284         'collapse' : true,
15285         /**
15286          * @event beforeselect
15287          * Fires before a list item is selected. Return false to cancel the selection.
15288         * @param {Roo.bootstrap.ComboBox} combo This combo box
15289         * @param {Roo.data.Record} record The data record returned from the underlying store
15290         * @param {Number} index The index of the selected item in the dropdown list
15291         */
15292         'beforeselect' : true,
15293         /**
15294          * @event select
15295          * Fires when a list item is selected
15296         * @param {Roo.bootstrap.ComboBox} combo This combo box
15297         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15298         * @param {Number} index The index of the selected item in the dropdown list
15299         */
15300         'select' : true,
15301         /**
15302          * @event beforequery
15303          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15304          * The event object passed has these properties:
15305         * @param {Roo.bootstrap.ComboBox} combo This combo box
15306         * @param {String} query The query
15307         * @param {Boolean} forceAll true to force "all" query
15308         * @param {Boolean} cancel true to cancel the query
15309         * @param {Object} e The query event object
15310         */
15311         'beforequery': true,
15312          /**
15313          * @event add
15314          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15315         * @param {Roo.bootstrap.ComboBox} combo This combo box
15316         */
15317         'add' : true,
15318         /**
15319          * @event edit
15320          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15321         * @param {Roo.bootstrap.ComboBox} combo This combo box
15322         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15323         */
15324         'edit' : true,
15325         /**
15326          * @event remove
15327          * Fires when the remove value from the combobox array
15328         * @param {Roo.bootstrap.ComboBox} combo This combo box
15329         */
15330         'remove' : true,
15331         /**
15332          * @event afterremove
15333          * Fires when the remove value from the combobox array
15334         * @param {Roo.bootstrap.ComboBox} combo This combo box
15335         */
15336         'afterremove' : true,
15337         /**
15338          * @event specialfilter
15339          * Fires when specialfilter
15340             * @param {Roo.bootstrap.ComboBox} combo This combo box
15341             */
15342         'specialfilter' : true,
15343         /**
15344          * @event tick
15345          * Fires when tick the element
15346             * @param {Roo.bootstrap.ComboBox} combo This combo box
15347             */
15348         'tick' : true,
15349         /**
15350          * @event touchviewdisplay
15351          * Fires when touch view require special display (default is using displayField)
15352             * @param {Roo.bootstrap.ComboBox} combo This combo box
15353             * @param {Object} cfg set html .
15354             */
15355         'touchviewdisplay' : true
15356         
15357     });
15358     
15359     this.item = [];
15360     this.tickItems = [];
15361     
15362     this.selectedIndex = -1;
15363     if(this.mode == 'local'){
15364         if(config.queryDelay === undefined){
15365             this.queryDelay = 10;
15366         }
15367         if(config.minChars === undefined){
15368             this.minChars = 0;
15369         }
15370     }
15371 };
15372
15373 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15374      
15375     /**
15376      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15377      * rendering into an Roo.Editor, defaults to false)
15378      */
15379     /**
15380      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15381      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15382      */
15383     /**
15384      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15385      */
15386     /**
15387      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15388      * the dropdown list (defaults to undefined, with no header element)
15389      */
15390
15391      /**
15392      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15393      */
15394      
15395      /**
15396      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15397      */
15398     listWidth: undefined,
15399     /**
15400      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15401      * mode = 'remote' or 'text' if mode = 'local')
15402      */
15403     displayField: undefined,
15404     
15405     /**
15406      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15407      * mode = 'remote' or 'value' if mode = 'local'). 
15408      * Note: use of a valueField requires the user make a selection
15409      * in order for a value to be mapped.
15410      */
15411     valueField: undefined,
15412     /**
15413      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15414      */
15415     modalTitle : '',
15416     
15417     /**
15418      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15419      * field's data value (defaults to the underlying DOM element's name)
15420      */
15421     hiddenName: undefined,
15422     /**
15423      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15424      */
15425     listClass: '',
15426     /**
15427      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15428      */
15429     selectedClass: 'active',
15430     
15431     /**
15432      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15433      */
15434     shadow:'sides',
15435     /**
15436      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15437      * anchor positions (defaults to 'tl-bl')
15438      */
15439     listAlign: 'tl-bl?',
15440     /**
15441      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15442      */
15443     maxHeight: 300,
15444     /**
15445      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15446      * query specified by the allQuery config option (defaults to 'query')
15447      */
15448     triggerAction: 'query',
15449     /**
15450      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15451      * (defaults to 4, does not apply if editable = false)
15452      */
15453     minChars : 4,
15454     /**
15455      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15456      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15457      */
15458     typeAhead: false,
15459     /**
15460      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15461      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15462      */
15463     queryDelay: 500,
15464     /**
15465      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15466      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15467      */
15468     pageSize: 0,
15469     /**
15470      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15471      * when editable = true (defaults to false)
15472      */
15473     selectOnFocus:false,
15474     /**
15475      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15476      */
15477     queryParam: 'query',
15478     /**
15479      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15480      * when mode = 'remote' (defaults to 'Loading...')
15481      */
15482     loadingText: 'Loading...',
15483     /**
15484      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15485      */
15486     resizable: false,
15487     /**
15488      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15489      */
15490     handleHeight : 8,
15491     /**
15492      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15493      * traditional select (defaults to true)
15494      */
15495     editable: true,
15496     /**
15497      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15498      */
15499     allQuery: '',
15500     /**
15501      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15502      */
15503     mode: 'remote',
15504     /**
15505      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15506      * listWidth has a higher value)
15507      */
15508     minListWidth : 70,
15509     /**
15510      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15511      * allow the user to set arbitrary text into the field (defaults to false)
15512      */
15513     forceSelection:false,
15514     /**
15515      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15516      * if typeAhead = true (defaults to 250)
15517      */
15518     typeAheadDelay : 250,
15519     /**
15520      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15521      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15522      */
15523     valueNotFoundText : undefined,
15524     /**
15525      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15526      */
15527     blockFocus : false,
15528     
15529     /**
15530      * @cfg {Boolean} disableClear Disable showing of clear button.
15531      */
15532     disableClear : false,
15533     /**
15534      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15535      */
15536     alwaysQuery : false,
15537     
15538     /**
15539      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15540      */
15541     multiple : false,
15542     
15543     /**
15544      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15545      */
15546     invalidClass : "has-warning",
15547     
15548     /**
15549      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15550      */
15551     validClass : "has-success",
15552     
15553     /**
15554      * @cfg {Boolean} specialFilter (true|false) special filter default false
15555      */
15556     specialFilter : false,
15557     
15558     /**
15559      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15560      */
15561     mobileTouchView : true,
15562     
15563     /**
15564      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15565      */
15566     useNativeIOS : false,
15567     
15568     /**
15569      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15570      */
15571     mobile_restrict_height : false,
15572     
15573     ios_options : false,
15574     
15575     //private
15576     addicon : false,
15577     editicon: false,
15578     
15579     page: 0,
15580     hasQuery: false,
15581     append: false,
15582     loadNext: false,
15583     autoFocus : true,
15584     tickable : false,
15585     btnPosition : 'right',
15586     triggerList : true,
15587     showToggleBtn : true,
15588     animate : true,
15589     emptyResultText: 'Empty',
15590     triggerText : 'Select',
15591     emptyTitle : '',
15592     width : false,
15593     
15594     // element that contains real text value.. (when hidden is used..)
15595     
15596     getAutoCreate : function()
15597     {   
15598         var cfg = false;
15599         //render
15600         /*
15601          * Render classic select for iso
15602          */
15603         
15604         if(Roo.isIOS && this.useNativeIOS){
15605             cfg = this.getAutoCreateNativeIOS();
15606             return cfg;
15607         }
15608         
15609         /*
15610          * Touch Devices
15611          */
15612         
15613         if(Roo.isTouch && this.mobileTouchView){
15614             cfg = this.getAutoCreateTouchView();
15615             return cfg;;
15616         }
15617         
15618         /*
15619          *  Normal ComboBox
15620          */
15621         if(!this.tickable){
15622             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15623             return cfg;
15624         }
15625         
15626         /*
15627          *  ComboBox with tickable selections
15628          */
15629              
15630         var align = this.labelAlign || this.parentLabelAlign();
15631         
15632         cfg = {
15633             cls : 'form-group roo-combobox-tickable' //input-group
15634         };
15635         
15636         var btn_text_select = '';
15637         var btn_text_done = '';
15638         var btn_text_cancel = '';
15639         
15640         if (this.btn_text_show) {
15641             btn_text_select = 'Select';
15642             btn_text_done = 'Done';
15643             btn_text_cancel = 'Cancel'; 
15644         }
15645         
15646         var buttons = {
15647             tag : 'div',
15648             cls : 'tickable-buttons',
15649             cn : [
15650                 {
15651                     tag : 'button',
15652                     type : 'button',
15653                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15654                     //html : this.triggerText
15655                     html: btn_text_select
15656                 },
15657                 {
15658                     tag : 'button',
15659                     type : 'button',
15660                     name : 'ok',
15661                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15662                     //html : 'Done'
15663                     html: btn_text_done
15664                 },
15665                 {
15666                     tag : 'button',
15667                     type : 'button',
15668                     name : 'cancel',
15669                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15670                     //html : 'Cancel'
15671                     html: btn_text_cancel
15672                 }
15673             ]
15674         };
15675         
15676         if(this.editable){
15677             buttons.cn.unshift({
15678                 tag: 'input',
15679                 cls: 'roo-select2-search-field-input'
15680             });
15681         }
15682         
15683         var _this = this;
15684         
15685         Roo.each(buttons.cn, function(c){
15686             if (_this.size) {
15687                 c.cls += ' btn-' + _this.size;
15688             }
15689
15690             if (_this.disabled) {
15691                 c.disabled = true;
15692             }
15693         });
15694         
15695         var box = {
15696             tag: 'div',
15697             style : 'display: contents',
15698             cn: [
15699                 {
15700                     tag: 'input',
15701                     type : 'hidden',
15702                     cls: 'form-hidden-field'
15703                 },
15704                 {
15705                     tag: 'ul',
15706                     cls: 'roo-select2-choices',
15707                     cn:[
15708                         {
15709                             tag: 'li',
15710                             cls: 'roo-select2-search-field',
15711                             cn: [
15712                                 buttons
15713                             ]
15714                         }
15715                     ]
15716                 }
15717             ]
15718         };
15719         
15720         var combobox = {
15721             cls: 'roo-select2-container input-group roo-select2-container-multi',
15722             cn: [
15723                 
15724                 box
15725 //                {
15726 //                    tag: 'ul',
15727 //                    cls: 'typeahead typeahead-long dropdown-menu',
15728 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15729 //                }
15730             ]
15731         };
15732         
15733         if(this.hasFeedback && !this.allowBlank){
15734             
15735             var feedback = {
15736                 tag: 'span',
15737                 cls: 'glyphicon form-control-feedback'
15738             };
15739
15740             combobox.cn.push(feedback);
15741         }
15742         
15743         
15744         
15745         var indicator = {
15746             tag : 'i',
15747             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15748             tooltip : 'This field is required'
15749         };
15750         if (Roo.bootstrap.version == 4) {
15751             indicator = {
15752                 tag : 'i',
15753                 style : 'display:none'
15754             };
15755         }
15756         if (align ==='left' && this.fieldLabel.length) {
15757             
15758             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15759             
15760             cfg.cn = [
15761                 indicator,
15762                 {
15763                     tag: 'label',
15764                     'for' :  id,
15765                     cls : 'control-label col-form-label',
15766                     html : this.fieldLabel
15767
15768                 },
15769                 {
15770                     cls : "", 
15771                     cn: [
15772                         combobox
15773                     ]
15774                 }
15775
15776             ];
15777             
15778             var labelCfg = cfg.cn[1];
15779             var contentCfg = cfg.cn[2];
15780             
15781
15782             if(this.indicatorpos == 'right'){
15783                 
15784                 cfg.cn = [
15785                     {
15786                         tag: 'label',
15787                         'for' :  id,
15788                         cls : 'control-label col-form-label',
15789                         cn : [
15790                             {
15791                                 tag : 'span',
15792                                 html : this.fieldLabel
15793                             },
15794                             indicator
15795                         ]
15796                     },
15797                     {
15798                         cls : "",
15799                         cn: [
15800                             combobox
15801                         ]
15802                     }
15803
15804                 ];
15805                 
15806                 
15807                 
15808                 labelCfg = cfg.cn[0];
15809                 contentCfg = cfg.cn[1];
15810             
15811             }
15812             
15813             if(this.labelWidth > 12){
15814                 labelCfg.style = "width: " + this.labelWidth + 'px';
15815             }
15816             if(this.width * 1 > 0){
15817                 contentCfg.style = "width: " + this.width + 'px';
15818             }
15819             if(this.labelWidth < 13 && this.labelmd == 0){
15820                 this.labelmd = this.labelWidth;
15821             }
15822             
15823             if(this.labellg > 0){
15824                 labelCfg.cls += ' col-lg-' + this.labellg;
15825                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15826             }
15827             
15828             if(this.labelmd > 0){
15829                 labelCfg.cls += ' col-md-' + this.labelmd;
15830                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15831             }
15832             
15833             if(this.labelsm > 0){
15834                 labelCfg.cls += ' col-sm-' + this.labelsm;
15835                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15836             }
15837             
15838             if(this.labelxs > 0){
15839                 labelCfg.cls += ' col-xs-' + this.labelxs;
15840                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15841             }
15842                 
15843                 
15844         } else if ( this.fieldLabel.length) {
15845 //                Roo.log(" label");
15846                  cfg.cn = [
15847                    indicator,
15848                     {
15849                         tag: 'label',
15850                         //cls : 'input-group-addon',
15851                         html : this.fieldLabel
15852                     },
15853                     combobox
15854                 ];
15855                 
15856                 if(this.indicatorpos == 'right'){
15857                     cfg.cn = [
15858                         {
15859                             tag: 'label',
15860                             //cls : 'input-group-addon',
15861                             html : this.fieldLabel
15862                         },
15863                         indicator,
15864                         combobox
15865                     ];
15866                     
15867                 }
15868
15869         } else {
15870             
15871 //                Roo.log(" no label && no align");
15872                 cfg = combobox
15873                      
15874                 
15875         }
15876          
15877         var settings=this;
15878         ['xs','sm','md','lg'].map(function(size){
15879             if (settings[size]) {
15880                 cfg.cls += ' col-' + size + '-' + settings[size];
15881             }
15882         });
15883         
15884         return cfg;
15885         
15886     },
15887     
15888     _initEventsCalled : false,
15889     
15890     // private
15891     initEvents: function()
15892     {   
15893         if (this._initEventsCalled) { // as we call render... prevent looping...
15894             return;
15895         }
15896         this._initEventsCalled = true;
15897         
15898         if (!this.store) {
15899             throw "can not find store for combo";
15900         }
15901         
15902         this.indicator = this.indicatorEl();
15903         
15904         this.store = Roo.factory(this.store, Roo.data);
15905         this.store.parent = this;
15906         
15907         // if we are building from html. then this element is so complex, that we can not really
15908         // use the rendered HTML.
15909         // so we have to trash and replace the previous code.
15910         if (Roo.XComponent.build_from_html) {
15911             // remove this element....
15912             var e = this.el.dom, k=0;
15913             while (e ) { e = e.previousSibling;  ++k;}
15914
15915             this.el.remove();
15916             
15917             this.el=false;
15918             this.rendered = false;
15919             
15920             this.render(this.parent().getChildContainer(true), k);
15921         }
15922         
15923         if(Roo.isIOS && this.useNativeIOS){
15924             this.initIOSView();
15925             return;
15926         }
15927         
15928         /*
15929          * Touch Devices
15930          */
15931         
15932         if(Roo.isTouch && this.mobileTouchView){
15933             this.initTouchView();
15934             return;
15935         }
15936         
15937         if(this.tickable){
15938             this.initTickableEvents();
15939             return;
15940         }
15941         
15942         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15943         
15944         if(this.hiddenName){
15945             
15946             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15947             
15948             this.hiddenField.dom.value =
15949                 this.hiddenValue !== undefined ? this.hiddenValue :
15950                 this.value !== undefined ? this.value : '';
15951
15952             // prevent input submission
15953             this.el.dom.removeAttribute('name');
15954             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15955              
15956              
15957         }
15958         //if(Roo.isGecko){
15959         //    this.el.dom.setAttribute('autocomplete', 'off');
15960         //}
15961         
15962         var cls = 'x-combo-list';
15963         
15964         //this.list = new Roo.Layer({
15965         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15966         //});
15967         
15968         var _this = this;
15969         
15970         (function(){
15971             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15972             _this.list.setWidth(lw);
15973         }).defer(100);
15974         
15975         this.list.on('mouseover', this.onViewOver, this);
15976         this.list.on('mousemove', this.onViewMove, this);
15977         this.list.on('scroll', this.onViewScroll, this);
15978         
15979         /*
15980         this.list.swallowEvent('mousewheel');
15981         this.assetHeight = 0;
15982
15983         if(this.title){
15984             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15985             this.assetHeight += this.header.getHeight();
15986         }
15987
15988         this.innerList = this.list.createChild({cls:cls+'-inner'});
15989         this.innerList.on('mouseover', this.onViewOver, this);
15990         this.innerList.on('mousemove', this.onViewMove, this);
15991         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15992         
15993         if(this.allowBlank && !this.pageSize && !this.disableClear){
15994             this.footer = this.list.createChild({cls:cls+'-ft'});
15995             this.pageTb = new Roo.Toolbar(this.footer);
15996            
15997         }
15998         if(this.pageSize){
15999             this.footer = this.list.createChild({cls:cls+'-ft'});
16000             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16001                     {pageSize: this.pageSize});
16002             
16003         }
16004         
16005         if (this.pageTb && this.allowBlank && !this.disableClear) {
16006             var _this = this;
16007             this.pageTb.add(new Roo.Toolbar.Fill(), {
16008                 cls: 'x-btn-icon x-btn-clear',
16009                 text: '&#160;',
16010                 handler: function()
16011                 {
16012                     _this.collapse();
16013                     _this.clearValue();
16014                     _this.onSelect(false, -1);
16015                 }
16016             });
16017         }
16018         if (this.footer) {
16019             this.assetHeight += this.footer.getHeight();
16020         }
16021         */
16022             
16023         if(!this.tpl){
16024             this.tpl = Roo.bootstrap.version == 4 ?
16025                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16026                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16027         }
16028
16029         this.view = new Roo.View(this.list, this.tpl, {
16030             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16031         });
16032         //this.view.wrapEl.setDisplayed(false);
16033         this.view.on('click', this.onViewClick, this);
16034         
16035         
16036         this.store.on('beforeload', this.onBeforeLoad, this);
16037         this.store.on('load', this.onLoad, this);
16038         this.store.on('loadexception', this.onLoadException, this);
16039         /*
16040         if(this.resizable){
16041             this.resizer = new Roo.Resizable(this.list,  {
16042                pinned:true, handles:'se'
16043             });
16044             this.resizer.on('resize', function(r, w, h){
16045                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16046                 this.listWidth = w;
16047                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16048                 this.restrictHeight();
16049             }, this);
16050             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16051         }
16052         */
16053         if(!this.editable){
16054             this.editable = true;
16055             this.setEditable(false);
16056         }
16057         
16058         /*
16059         
16060         if (typeof(this.events.add.listeners) != 'undefined') {
16061             
16062             this.addicon = this.wrap.createChild(
16063                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16064        
16065             this.addicon.on('click', function(e) {
16066                 this.fireEvent('add', this);
16067             }, this);
16068         }
16069         if (typeof(this.events.edit.listeners) != 'undefined') {
16070             
16071             this.editicon = this.wrap.createChild(
16072                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16073             if (this.addicon) {
16074                 this.editicon.setStyle('margin-left', '40px');
16075             }
16076             this.editicon.on('click', function(e) {
16077                 
16078                 // we fire even  if inothing is selected..
16079                 this.fireEvent('edit', this, this.lastData );
16080                 
16081             }, this);
16082         }
16083         */
16084         
16085         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16086             "up" : function(e){
16087                 this.inKeyMode = true;
16088                 this.selectPrev();
16089             },
16090
16091             "down" : function(e){
16092                 if(!this.isExpanded()){
16093                     this.onTriggerClick();
16094                 }else{
16095                     this.inKeyMode = true;
16096                     this.selectNext();
16097                 }
16098             },
16099
16100             "enter" : function(e){
16101 //                this.onViewClick();
16102                 //return true;
16103                 this.collapse();
16104                 
16105                 if(this.fireEvent("specialkey", this, e)){
16106                     this.onViewClick(false);
16107                 }
16108                 
16109                 return true;
16110             },
16111
16112             "esc" : function(e){
16113                 this.collapse();
16114             },
16115
16116             "tab" : function(e){
16117                 this.collapse();
16118                 
16119                 if(this.fireEvent("specialkey", this, e)){
16120                     this.onViewClick(false);
16121                 }
16122                 
16123                 return true;
16124             },
16125
16126             scope : this,
16127
16128             doRelay : function(foo, bar, hname){
16129                 if(hname == 'down' || this.scope.isExpanded()){
16130                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16131                 }
16132                 return true;
16133             },
16134
16135             forceKeyDown: true
16136         });
16137         
16138         
16139         this.queryDelay = Math.max(this.queryDelay || 10,
16140                 this.mode == 'local' ? 10 : 250);
16141         
16142         
16143         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16144         
16145         if(this.typeAhead){
16146             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16147         }
16148         if(this.editable !== false){
16149             this.inputEl().on("keyup", this.onKeyUp, this);
16150         }
16151         if(this.forceSelection){
16152             this.inputEl().on('blur', this.doForce, this);
16153         }
16154         
16155         if(this.multiple){
16156             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16157             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16158         }
16159     },
16160     
16161     initTickableEvents: function()
16162     {   
16163         this.createList();
16164         
16165         if(this.hiddenName){
16166             
16167             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16168             
16169             this.hiddenField.dom.value =
16170                 this.hiddenValue !== undefined ? this.hiddenValue :
16171                 this.value !== undefined ? this.value : '';
16172
16173             // prevent input submission
16174             this.el.dom.removeAttribute('name');
16175             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16176              
16177              
16178         }
16179         
16180 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16181         
16182         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16183         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16184         if(this.triggerList){
16185             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16186         }
16187          
16188         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16189         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16190         
16191         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16192         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16193         
16194         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16195         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16196         
16197         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16198         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16199         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16200         
16201         this.okBtn.hide();
16202         this.cancelBtn.hide();
16203         
16204         var _this = this;
16205         
16206         (function(){
16207             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16208             _this.list.setWidth(lw);
16209         }).defer(100);
16210         
16211         this.list.on('mouseover', this.onViewOver, this);
16212         this.list.on('mousemove', this.onViewMove, this);
16213         
16214         this.list.on('scroll', this.onViewScroll, this);
16215         
16216         if(!this.tpl){
16217             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16218                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16219         }
16220
16221         this.view = new Roo.View(this.list, this.tpl, {
16222             singleSelect:true,
16223             tickable:true,
16224             parent:this,
16225             store: this.store,
16226             selectedClass: this.selectedClass
16227         });
16228         
16229         //this.view.wrapEl.setDisplayed(false);
16230         this.view.on('click', this.onViewClick, this);
16231         
16232         
16233         
16234         this.store.on('beforeload', this.onBeforeLoad, this);
16235         this.store.on('load', this.onLoad, this);
16236         this.store.on('loadexception', this.onLoadException, this);
16237         
16238         if(this.editable){
16239             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16240                 "up" : function(e){
16241                     this.inKeyMode = true;
16242                     this.selectPrev();
16243                 },
16244
16245                 "down" : function(e){
16246                     this.inKeyMode = true;
16247                     this.selectNext();
16248                 },
16249
16250                 "enter" : function(e){
16251                     if(this.fireEvent("specialkey", this, e)){
16252                         this.onViewClick(false);
16253                     }
16254                     
16255                     return true;
16256                 },
16257
16258                 "esc" : function(e){
16259                     this.onTickableFooterButtonClick(e, false, false);
16260                 },
16261
16262                 "tab" : function(e){
16263                     this.fireEvent("specialkey", this, e);
16264                     
16265                     this.onTickableFooterButtonClick(e, false, false);
16266                     
16267                     return true;
16268                 },
16269
16270                 scope : this,
16271
16272                 doRelay : function(e, fn, key){
16273                     if(this.scope.isExpanded()){
16274                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16275                     }
16276                     return true;
16277                 },
16278
16279                 forceKeyDown: true
16280             });
16281         }
16282         
16283         this.queryDelay = Math.max(this.queryDelay || 10,
16284                 this.mode == 'local' ? 10 : 250);
16285         
16286         
16287         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16288         
16289         if(this.typeAhead){
16290             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16291         }
16292         
16293         if(this.editable !== false){
16294             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16295         }
16296         
16297         this.indicator = this.indicatorEl();
16298         
16299         if(this.indicator){
16300             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16301             this.indicator.hide();
16302         }
16303         
16304     },
16305
16306     onDestroy : function(){
16307         if(this.view){
16308             this.view.setStore(null);
16309             this.view.el.removeAllListeners();
16310             this.view.el.remove();
16311             this.view.purgeListeners();
16312         }
16313         if(this.list){
16314             this.list.dom.innerHTML  = '';
16315         }
16316         
16317         if(this.store){
16318             this.store.un('beforeload', this.onBeforeLoad, this);
16319             this.store.un('load', this.onLoad, this);
16320             this.store.un('loadexception', this.onLoadException, this);
16321         }
16322         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16323     },
16324
16325     // private
16326     fireKey : function(e){
16327         if(e.isNavKeyPress() && !this.list.isVisible()){
16328             this.fireEvent("specialkey", this, e);
16329         }
16330     },
16331
16332     // private
16333     onResize: function(w, h)
16334     {
16335         
16336         
16337 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16338 //        
16339 //        if(typeof w != 'number'){
16340 //            // we do not handle it!?!?
16341 //            return;
16342 //        }
16343 //        var tw = this.trigger.getWidth();
16344 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16345 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16346 //        var x = w - tw;
16347 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16348 //            
16349 //        //this.trigger.setStyle('left', x+'px');
16350 //        
16351 //        if(this.list && this.listWidth === undefined){
16352 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16353 //            this.list.setWidth(lw);
16354 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16355 //        }
16356         
16357     
16358         
16359     },
16360
16361     /**
16362      * Allow or prevent the user from directly editing the field text.  If false is passed,
16363      * the user will only be able to select from the items defined in the dropdown list.  This method
16364      * is the runtime equivalent of setting the 'editable' config option at config time.
16365      * @param {Boolean} value True to allow the user to directly edit the field text
16366      */
16367     setEditable : function(value){
16368         if(value == this.editable){
16369             return;
16370         }
16371         this.editable = value;
16372         if(!value){
16373             this.inputEl().dom.setAttribute('readOnly', true);
16374             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16375             this.inputEl().addClass('x-combo-noedit');
16376         }else{
16377             this.inputEl().dom.setAttribute('readOnly', false);
16378             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16379             this.inputEl().removeClass('x-combo-noedit');
16380         }
16381     },
16382
16383     // private
16384     
16385     onBeforeLoad : function(combo,opts){
16386         if(!this.hasFocus){
16387             return;
16388         }
16389          if (!opts.add) {
16390             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16391          }
16392         this.restrictHeight();
16393         this.selectedIndex = -1;
16394     },
16395
16396     // private
16397     onLoad : function(){
16398         
16399         this.hasQuery = false;
16400         
16401         if(!this.hasFocus){
16402             return;
16403         }
16404         
16405         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16406             this.loading.hide();
16407         }
16408         
16409         if(this.store.getCount() > 0){
16410             
16411             this.expand();
16412             this.restrictHeight();
16413             if(this.lastQuery == this.allQuery){
16414                 if(this.editable && !this.tickable){
16415                     this.inputEl().dom.select();
16416                 }
16417                 
16418                 if(
16419                     !this.selectByValue(this.value, true) &&
16420                     this.autoFocus && 
16421                     (
16422                         !this.store.lastOptions ||
16423                         typeof(this.store.lastOptions.add) == 'undefined' || 
16424                         this.store.lastOptions.add != true
16425                     )
16426                 ){
16427                     this.select(0, true);
16428                 }
16429             }else{
16430                 if(this.autoFocus){
16431                     this.selectNext();
16432                 }
16433                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16434                     this.taTask.delay(this.typeAheadDelay);
16435                 }
16436             }
16437         }else{
16438             this.onEmptyResults();
16439         }
16440         
16441         //this.el.focus();
16442     },
16443     // private
16444     onLoadException : function()
16445     {
16446         this.hasQuery = false;
16447         
16448         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16449             this.loading.hide();
16450         }
16451         
16452         if(this.tickable && this.editable){
16453             return;
16454         }
16455         
16456         this.collapse();
16457         // only causes errors at present
16458         //Roo.log(this.store.reader.jsonData);
16459         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16460             // fixme
16461             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16462         //}
16463         
16464         
16465     },
16466     // private
16467     onTypeAhead : function(){
16468         if(this.store.getCount() > 0){
16469             var r = this.store.getAt(0);
16470             var newValue = r.data[this.displayField];
16471             var len = newValue.length;
16472             var selStart = this.getRawValue().length;
16473             
16474             if(selStart != len){
16475                 this.setRawValue(newValue);
16476                 this.selectText(selStart, newValue.length);
16477             }
16478         }
16479     },
16480
16481     // private
16482     onSelect : function(record, index){
16483         
16484         if(this.fireEvent('beforeselect', this, record, index) !== false){
16485         
16486             this.setFromData(index > -1 ? record.data : false);
16487             
16488             this.collapse();
16489             this.fireEvent('select', this, record, index);
16490         }
16491     },
16492
16493     /**
16494      * Returns the currently selected field value or empty string if no value is set.
16495      * @return {String} value The selected value
16496      */
16497     getValue : function()
16498     {
16499         if(Roo.isIOS && this.useNativeIOS){
16500             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16501         }
16502         
16503         if(this.multiple){
16504             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16505         }
16506         
16507         if(this.valueField){
16508             return typeof this.value != 'undefined' ? this.value : '';
16509         }else{
16510             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16511         }
16512     },
16513     
16514     getRawValue : function()
16515     {
16516         if(Roo.isIOS && this.useNativeIOS){
16517             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16518         }
16519         
16520         var v = this.inputEl().getValue();
16521         
16522         return v;
16523     },
16524
16525     /**
16526      * Clears any text/value currently set in the field
16527      */
16528     clearValue : function(){
16529         
16530         if(this.hiddenField){
16531             this.hiddenField.dom.value = '';
16532         }
16533         this.value = '';
16534         this.setRawValue('');
16535         this.lastSelectionText = '';
16536         this.lastData = false;
16537         
16538         var close = this.closeTriggerEl();
16539         
16540         if(close){
16541             close.hide();
16542         }
16543         
16544         this.validate();
16545         
16546     },
16547
16548     /**
16549      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16550      * will be displayed in the field.  If the value does not match the data value of an existing item,
16551      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16552      * Otherwise the field will be blank (although the value will still be set).
16553      * @param {String} value The value to match
16554      */
16555     setValue : function(v)
16556     {
16557         if(Roo.isIOS && this.useNativeIOS){
16558             this.setIOSValue(v);
16559             return;
16560         }
16561         
16562         if(this.multiple){
16563             this.syncValue();
16564             return;
16565         }
16566         
16567         var text = v;
16568         if(this.valueField){
16569             var r = this.findRecord(this.valueField, v);
16570             if(r){
16571                 text = r.data[this.displayField];
16572             }else if(this.valueNotFoundText !== undefined){
16573                 text = this.valueNotFoundText;
16574             }
16575         }
16576         this.lastSelectionText = text;
16577         if(this.hiddenField){
16578             this.hiddenField.dom.value = v;
16579         }
16580         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16581         this.value = v;
16582         
16583         var close = this.closeTriggerEl();
16584         
16585         if(close){
16586             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16587         }
16588         
16589         this.validate();
16590     },
16591     /**
16592      * @property {Object} the last set data for the element
16593      */
16594     
16595     lastData : false,
16596     /**
16597      * Sets the value of the field based on a object which is related to the record format for the store.
16598      * @param {Object} value the value to set as. or false on reset?
16599      */
16600     setFromData : function(o){
16601         
16602         if(this.multiple){
16603             this.addItem(o);
16604             return;
16605         }
16606             
16607         var dv = ''; // display value
16608         var vv = ''; // value value..
16609         this.lastData = o;
16610         if (this.displayField) {
16611             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16612         } else {
16613             // this is an error condition!!!
16614             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16615         }
16616         
16617         if(this.valueField){
16618             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16619         }
16620         
16621         var close = this.closeTriggerEl();
16622         
16623         if(close){
16624             if(dv.length || vv * 1 > 0){
16625                 close.show() ;
16626                 this.blockFocus=true;
16627             } else {
16628                 close.hide();
16629             }             
16630         }
16631         
16632         if(this.hiddenField){
16633             this.hiddenField.dom.value = vv;
16634             
16635             this.lastSelectionText = dv;
16636             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16637             this.value = vv;
16638             return;
16639         }
16640         // no hidden field.. - we store the value in 'value', but still display
16641         // display field!!!!
16642         this.lastSelectionText = dv;
16643         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16644         this.value = vv;
16645         
16646         
16647         
16648     },
16649     // private
16650     reset : function(){
16651         // overridden so that last data is reset..
16652         
16653         if(this.multiple){
16654             this.clearItem();
16655             return;
16656         }
16657         
16658         this.setValue(this.originalValue);
16659         //this.clearInvalid();
16660         this.lastData = false;
16661         if (this.view) {
16662             this.view.clearSelections();
16663         }
16664         
16665         this.validate();
16666     },
16667     // private
16668     findRecord : function(prop, value){
16669         var record;
16670         if(this.store.getCount() > 0){
16671             this.store.each(function(r){
16672                 if(r.data[prop] == value){
16673                     record = r;
16674                     return false;
16675                 }
16676                 return true;
16677             });
16678         }
16679         return record;
16680     },
16681     
16682     getName: function()
16683     {
16684         // returns hidden if it's set..
16685         if (!this.rendered) {return ''};
16686         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16687         
16688     },
16689     // private
16690     onViewMove : function(e, t){
16691         this.inKeyMode = false;
16692     },
16693
16694     // private
16695     onViewOver : function(e, t){
16696         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16697             return;
16698         }
16699         var item = this.view.findItemFromChild(t);
16700         
16701         if(item){
16702             var index = this.view.indexOf(item);
16703             this.select(index, false);
16704         }
16705     },
16706
16707     // private
16708     onViewClick : function(view, doFocus, el, e)
16709     {
16710         var index = this.view.getSelectedIndexes()[0];
16711         
16712         var r = this.store.getAt(index);
16713         
16714         if(this.tickable){
16715             
16716             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16717                 return;
16718             }
16719             
16720             var rm = false;
16721             var _this = this;
16722             
16723             Roo.each(this.tickItems, function(v,k){
16724                 
16725                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16726                     Roo.log(v);
16727                     _this.tickItems.splice(k, 1);
16728                     
16729                     if(typeof(e) == 'undefined' && view == false){
16730                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16731                     }
16732                     
16733                     rm = true;
16734                     return;
16735                 }
16736             });
16737             
16738             if(rm){
16739                 return;
16740             }
16741             
16742             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16743                 this.tickItems.push(r.data);
16744             }
16745             
16746             if(typeof(e) == 'undefined' && view == false){
16747                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16748             }
16749                     
16750             return;
16751         }
16752         
16753         if(r){
16754             this.onSelect(r, index);
16755         }
16756         if(doFocus !== false && !this.blockFocus){
16757             this.inputEl().focus();
16758         }
16759     },
16760
16761     // private
16762     restrictHeight : function(){
16763         //this.innerList.dom.style.height = '';
16764         //var inner = this.innerList.dom;
16765         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16766         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16767         //this.list.beginUpdate();
16768         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16769         this.list.alignTo(this.inputEl(), this.listAlign);
16770         this.list.alignTo(this.inputEl(), this.listAlign);
16771         //this.list.endUpdate();
16772     },
16773
16774     // private
16775     onEmptyResults : function(){
16776         
16777         if(this.tickable && this.editable){
16778             this.hasFocus = false;
16779             this.restrictHeight();
16780             return;
16781         }
16782         
16783         this.collapse();
16784     },
16785
16786     /**
16787      * Returns true if the dropdown list is expanded, else false.
16788      */
16789     isExpanded : function(){
16790         return this.list.isVisible();
16791     },
16792
16793     /**
16794      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16795      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16796      * @param {String} value The data value of the item to select
16797      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16798      * selected item if it is not currently in view (defaults to true)
16799      * @return {Boolean} True if the value matched an item in the list, else false
16800      */
16801     selectByValue : function(v, scrollIntoView){
16802         if(v !== undefined && v !== null){
16803             var r = this.findRecord(this.valueField || this.displayField, v);
16804             if(r){
16805                 this.select(this.store.indexOf(r), scrollIntoView);
16806                 return true;
16807             }
16808         }
16809         return false;
16810     },
16811
16812     /**
16813      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16814      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16815      * @param {Number} index The zero-based index of the list item to select
16816      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16817      * selected item if it is not currently in view (defaults to true)
16818      */
16819     select : function(index, scrollIntoView){
16820         this.selectedIndex = index;
16821         this.view.select(index);
16822         if(scrollIntoView !== false){
16823             var el = this.view.getNode(index);
16824             /*
16825              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16826              */
16827             if(el){
16828                 this.list.scrollChildIntoView(el, false);
16829             }
16830         }
16831     },
16832
16833     // private
16834     selectNext : function(){
16835         var ct = this.store.getCount();
16836         if(ct > 0){
16837             if(this.selectedIndex == -1){
16838                 this.select(0);
16839             }else if(this.selectedIndex < ct-1){
16840                 this.select(this.selectedIndex+1);
16841             }
16842         }
16843     },
16844
16845     // private
16846     selectPrev : function(){
16847         var ct = this.store.getCount();
16848         if(ct > 0){
16849             if(this.selectedIndex == -1){
16850                 this.select(0);
16851             }else if(this.selectedIndex != 0){
16852                 this.select(this.selectedIndex-1);
16853             }
16854         }
16855     },
16856
16857     // private
16858     onKeyUp : function(e){
16859         if(this.editable !== false && !e.isSpecialKey()){
16860             this.lastKey = e.getKey();
16861             this.dqTask.delay(this.queryDelay);
16862         }
16863     },
16864
16865     // private
16866     validateBlur : function(){
16867         return !this.list || !this.list.isVisible();   
16868     },
16869
16870     // private
16871     initQuery : function(){
16872         
16873         var v = this.getRawValue();
16874         
16875         if(this.tickable && this.editable){
16876             v = this.tickableInputEl().getValue();
16877         }
16878         
16879         this.doQuery(v);
16880     },
16881
16882     // private
16883     doForce : function(){
16884         if(this.inputEl().dom.value.length > 0){
16885             this.inputEl().dom.value =
16886                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16887              
16888         }
16889     },
16890
16891     /**
16892      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16893      * query allowing the query action to be canceled if needed.
16894      * @param {String} query The SQL query to execute
16895      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16896      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16897      * saved in the current store (defaults to false)
16898      */
16899     doQuery : function(q, forceAll){
16900         
16901         if(q === undefined || q === null){
16902             q = '';
16903         }
16904         var qe = {
16905             query: q,
16906             forceAll: forceAll,
16907             combo: this,
16908             cancel:false
16909         };
16910         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16911             return false;
16912         }
16913         q = qe.query;
16914         
16915         forceAll = qe.forceAll;
16916         if(forceAll === true || (q.length >= this.minChars)){
16917             
16918             this.hasQuery = true;
16919             
16920             if(this.lastQuery != q || this.alwaysQuery){
16921                 this.lastQuery = q;
16922                 if(this.mode == 'local'){
16923                     this.selectedIndex = -1;
16924                     if(forceAll){
16925                         this.store.clearFilter();
16926                     }else{
16927                         
16928                         if(this.specialFilter){
16929                             this.fireEvent('specialfilter', this);
16930                             this.onLoad();
16931                             return;
16932                         }
16933                         
16934                         this.store.filter(this.displayField, q);
16935                     }
16936                     
16937                     this.store.fireEvent("datachanged", this.store);
16938                     
16939                     this.onLoad();
16940                     
16941                     
16942                 }else{
16943                     
16944                     this.store.baseParams[this.queryParam] = q;
16945                     
16946                     var options = {params : this.getParams(q)};
16947                     
16948                     if(this.loadNext){
16949                         options.add = true;
16950                         options.params.start = this.page * this.pageSize;
16951                     }
16952                     
16953                     this.store.load(options);
16954                     
16955                     /*
16956                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16957                      *  we should expand the list on onLoad
16958                      *  so command out it
16959                      */
16960 //                    this.expand();
16961                 }
16962             }else{
16963                 this.selectedIndex = -1;
16964                 this.onLoad();   
16965             }
16966         }
16967         
16968         this.loadNext = false;
16969     },
16970     
16971     // private
16972     getParams : function(q){
16973         var p = {};
16974         //p[this.queryParam] = q;
16975         
16976         if(this.pageSize){
16977             p.start = 0;
16978             p.limit = this.pageSize;
16979         }
16980         return p;
16981     },
16982
16983     /**
16984      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16985      */
16986     collapse : function(){
16987         if(!this.isExpanded()){
16988             return;
16989         }
16990         
16991         this.list.hide();
16992         
16993         this.hasFocus = false;
16994         
16995         if(this.tickable){
16996             this.okBtn.hide();
16997             this.cancelBtn.hide();
16998             this.trigger.show();
16999             
17000             if(this.editable){
17001                 this.tickableInputEl().dom.value = '';
17002                 this.tickableInputEl().blur();
17003             }
17004             
17005         }
17006         
17007         Roo.get(document).un('mousedown', this.collapseIf, this);
17008         Roo.get(document).un('mousewheel', this.collapseIf, this);
17009         if (!this.editable) {
17010             Roo.get(document).un('keydown', this.listKeyPress, this);
17011         }
17012         this.fireEvent('collapse', this);
17013         
17014         this.validate();
17015     },
17016
17017     // private
17018     collapseIf : function(e){
17019         var in_combo  = e.within(this.el);
17020         var in_list =  e.within(this.list);
17021         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17022         
17023         if (in_combo || in_list || is_list) {
17024             //e.stopPropagation();
17025             return;
17026         }
17027         
17028         if(this.tickable){
17029             this.onTickableFooterButtonClick(e, false, false);
17030         }
17031
17032         this.collapse();
17033         
17034     },
17035
17036     /**
17037      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17038      */
17039     expand : function(){
17040        
17041         if(this.isExpanded() || !this.hasFocus){
17042             return;
17043         }
17044         
17045         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17046         this.list.setWidth(lw);
17047         
17048         Roo.log('expand');
17049         
17050         this.list.show();
17051         
17052         this.restrictHeight();
17053         
17054         if(this.tickable){
17055             
17056             this.tickItems = Roo.apply([], this.item);
17057             
17058             this.okBtn.show();
17059             this.cancelBtn.show();
17060             this.trigger.hide();
17061             
17062             if(this.editable){
17063                 this.tickableInputEl().focus();
17064             }
17065             
17066         }
17067         
17068         Roo.get(document).on('mousedown', this.collapseIf, this);
17069         Roo.get(document).on('mousewheel', this.collapseIf, this);
17070         if (!this.editable) {
17071             Roo.get(document).on('keydown', this.listKeyPress, this);
17072         }
17073         
17074         this.fireEvent('expand', this);
17075     },
17076
17077     // private
17078     // Implements the default empty TriggerField.onTriggerClick function
17079     onTriggerClick : function(e)
17080     {
17081         Roo.log('trigger click');
17082         
17083         if(this.disabled || !this.triggerList){
17084             return;
17085         }
17086         
17087         this.page = 0;
17088         this.loadNext = false;
17089         
17090         if(this.isExpanded()){
17091             this.collapse();
17092             if (!this.blockFocus) {
17093                 this.inputEl().focus();
17094             }
17095             
17096         }else {
17097             this.hasFocus = true;
17098             if(this.triggerAction == 'all') {
17099                 this.doQuery(this.allQuery, true);
17100             } else {
17101                 this.doQuery(this.getRawValue());
17102             }
17103             if (!this.blockFocus) {
17104                 this.inputEl().focus();
17105             }
17106         }
17107     },
17108     
17109     onTickableTriggerClick : function(e)
17110     {
17111         if(this.disabled){
17112             return;
17113         }
17114         
17115         this.page = 0;
17116         this.loadNext = false;
17117         this.hasFocus = true;
17118         
17119         if(this.triggerAction == 'all') {
17120             this.doQuery(this.allQuery, true);
17121         } else {
17122             this.doQuery(this.getRawValue());
17123         }
17124     },
17125     
17126     onSearchFieldClick : function(e)
17127     {
17128         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17129             this.onTickableFooterButtonClick(e, false, false);
17130             return;
17131         }
17132         
17133         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17134             return;
17135         }
17136         
17137         this.page = 0;
17138         this.loadNext = false;
17139         this.hasFocus = true;
17140         
17141         if(this.triggerAction == 'all') {
17142             this.doQuery(this.allQuery, true);
17143         } else {
17144             this.doQuery(this.getRawValue());
17145         }
17146     },
17147     
17148     listKeyPress : function(e)
17149     {
17150         //Roo.log('listkeypress');
17151         // scroll to first matching element based on key pres..
17152         if (e.isSpecialKey()) {
17153             return false;
17154         }
17155         var k = String.fromCharCode(e.getKey()).toUpperCase();
17156         //Roo.log(k);
17157         var match  = false;
17158         var csel = this.view.getSelectedNodes();
17159         var cselitem = false;
17160         if (csel.length) {
17161             var ix = this.view.indexOf(csel[0]);
17162             cselitem  = this.store.getAt(ix);
17163             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17164                 cselitem = false;
17165             }
17166             
17167         }
17168         
17169         this.store.each(function(v) { 
17170             if (cselitem) {
17171                 // start at existing selection.
17172                 if (cselitem.id == v.id) {
17173                     cselitem = false;
17174                 }
17175                 return true;
17176             }
17177                 
17178             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17179                 match = this.store.indexOf(v);
17180                 return false;
17181             }
17182             return true;
17183         }, this);
17184         
17185         if (match === false) {
17186             return true; // no more action?
17187         }
17188         // scroll to?
17189         this.view.select(match);
17190         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17191         sn.scrollIntoView(sn.dom.parentNode, false);
17192     },
17193     
17194     onViewScroll : function(e, t){
17195         
17196         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){
17197             return;
17198         }
17199         
17200         this.hasQuery = true;
17201         
17202         this.loading = this.list.select('.loading', true).first();
17203         
17204         if(this.loading === null){
17205             this.list.createChild({
17206                 tag: 'div',
17207                 cls: 'loading roo-select2-more-results roo-select2-active',
17208                 html: 'Loading more results...'
17209             });
17210             
17211             this.loading = this.list.select('.loading', true).first();
17212             
17213             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17214             
17215             this.loading.hide();
17216         }
17217         
17218         this.loading.show();
17219         
17220         var _combo = this;
17221         
17222         this.page++;
17223         this.loadNext = true;
17224         
17225         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17226         
17227         return;
17228     },
17229     
17230     addItem : function(o)
17231     {   
17232         var dv = ''; // display value
17233         
17234         if (this.displayField) {
17235             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17236         } else {
17237             // this is an error condition!!!
17238             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17239         }
17240         
17241         if(!dv.length){
17242             return;
17243         }
17244         
17245         var choice = this.choices.createChild({
17246             tag: 'li',
17247             cls: 'roo-select2-search-choice',
17248             cn: [
17249                 {
17250                     tag: 'div',
17251                     html: dv
17252                 },
17253                 {
17254                     tag: 'a',
17255                     href: '#',
17256                     cls: 'roo-select2-search-choice-close fa fa-times',
17257                     tabindex: '-1'
17258                 }
17259             ]
17260             
17261         }, this.searchField);
17262         
17263         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17264         
17265         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17266         
17267         this.item.push(o);
17268         
17269         this.lastData = o;
17270         
17271         this.syncValue();
17272         
17273         this.inputEl().dom.value = '';
17274         
17275         this.validate();
17276     },
17277     
17278     onRemoveItem : function(e, _self, o)
17279     {
17280         e.preventDefault();
17281         
17282         this.lastItem = Roo.apply([], this.item);
17283         
17284         var index = this.item.indexOf(o.data) * 1;
17285         
17286         if( index < 0){
17287             Roo.log('not this item?!');
17288             return;
17289         }
17290         
17291         this.item.splice(index, 1);
17292         o.item.remove();
17293         
17294         this.syncValue();
17295         
17296         this.fireEvent('remove', this, e);
17297         
17298         this.validate();
17299         
17300     },
17301     
17302     syncValue : function()
17303     {
17304         if(!this.item.length){
17305             this.clearValue();
17306             return;
17307         }
17308             
17309         var value = [];
17310         var _this = this;
17311         Roo.each(this.item, function(i){
17312             if(_this.valueField){
17313                 value.push(i[_this.valueField]);
17314                 return;
17315             }
17316
17317             value.push(i);
17318         });
17319
17320         this.value = value.join(',');
17321
17322         if(this.hiddenField){
17323             this.hiddenField.dom.value = this.value;
17324         }
17325         
17326         this.store.fireEvent("datachanged", this.store);
17327         
17328         this.validate();
17329     },
17330     
17331     clearItem : function()
17332     {
17333         if(!this.multiple){
17334             return;
17335         }
17336         
17337         this.item = [];
17338         
17339         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17340            c.remove();
17341         });
17342         
17343         this.syncValue();
17344         
17345         this.validate();
17346         
17347         if(this.tickable && !Roo.isTouch){
17348             this.view.refresh();
17349         }
17350     },
17351     
17352     inputEl: function ()
17353     {
17354         if(Roo.isIOS && this.useNativeIOS){
17355             return this.el.select('select.roo-ios-select', true).first();
17356         }
17357         
17358         if(Roo.isTouch && this.mobileTouchView){
17359             return this.el.select('input.form-control',true).first();
17360         }
17361         
17362         if(this.tickable){
17363             return this.searchField;
17364         }
17365         
17366         return this.el.select('input.form-control',true).first();
17367     },
17368     
17369     onTickableFooterButtonClick : function(e, btn, el)
17370     {
17371         e.preventDefault();
17372         
17373         this.lastItem = Roo.apply([], this.item);
17374         
17375         if(btn && btn.name == 'cancel'){
17376             this.tickItems = Roo.apply([], this.item);
17377             this.collapse();
17378             return;
17379         }
17380         
17381         this.clearItem();
17382         
17383         var _this = this;
17384         
17385         Roo.each(this.tickItems, function(o){
17386             _this.addItem(o);
17387         });
17388         
17389         this.collapse();
17390         
17391     },
17392     
17393     validate : function()
17394     {
17395         if(this.getVisibilityEl().hasClass('hidden')){
17396             return true;
17397         }
17398         
17399         var v = this.getRawValue();
17400         
17401         if(this.multiple){
17402             v = this.getValue();
17403         }
17404         
17405         if(this.disabled || this.allowBlank || v.length){
17406             this.markValid();
17407             return true;
17408         }
17409         
17410         this.markInvalid();
17411         return false;
17412     },
17413     
17414     tickableInputEl : function()
17415     {
17416         if(!this.tickable || !this.editable){
17417             return this.inputEl();
17418         }
17419         
17420         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17421     },
17422     
17423     
17424     getAutoCreateTouchView : function()
17425     {
17426         var id = Roo.id();
17427         
17428         var cfg = {
17429             cls: 'form-group' //input-group
17430         };
17431         
17432         var input =  {
17433             tag: 'input',
17434             id : id,
17435             type : this.inputType,
17436             cls : 'form-control x-combo-noedit',
17437             autocomplete: 'new-password',
17438             placeholder : this.placeholder || '',
17439             readonly : true
17440         };
17441         
17442         if (this.name) {
17443             input.name = this.name;
17444         }
17445         
17446         if (this.size) {
17447             input.cls += ' input-' + this.size;
17448         }
17449         
17450         if (this.disabled) {
17451             input.disabled = true;
17452         }
17453         
17454         var inputblock = {
17455             cls : 'roo-combobox-wrap',
17456             cn : [
17457                 input
17458             ]
17459         };
17460         
17461         if(this.before){
17462             inputblock.cls += ' input-group';
17463             
17464             inputblock.cn.unshift({
17465                 tag :'span',
17466                 cls : 'input-group-addon input-group-prepend input-group-text',
17467                 html : this.before
17468             });
17469         }
17470         
17471         if(this.removable && !this.multiple){
17472             inputblock.cls += ' roo-removable';
17473             
17474             inputblock.cn.push({
17475                 tag: 'button',
17476                 html : 'x',
17477                 cls : 'roo-combo-removable-btn close'
17478             });
17479         }
17480
17481         if(this.hasFeedback && !this.allowBlank){
17482             
17483             inputblock.cls += ' has-feedback';
17484             
17485             inputblock.cn.push({
17486                 tag: 'span',
17487                 cls: 'glyphicon form-control-feedback'
17488             });
17489             
17490         }
17491         
17492         if (this.after) {
17493             
17494             inputblock.cls += (this.before) ? '' : ' input-group';
17495             
17496             inputblock.cn.push({
17497                 tag :'span',
17498                 cls : 'input-group-addon input-group-append input-group-text',
17499                 html : this.after
17500             });
17501         }
17502
17503         
17504         var ibwrap = inputblock;
17505         
17506         if(this.multiple){
17507             ibwrap = {
17508                 tag: 'ul',
17509                 cls: 'roo-select2-choices',
17510                 cn:[
17511                     {
17512                         tag: 'li',
17513                         cls: 'roo-select2-search-field',
17514                         cn: [
17515
17516                             inputblock
17517                         ]
17518                     }
17519                 ]
17520             };
17521         
17522             
17523         }
17524         
17525         var combobox = {
17526             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17527             cn: [
17528                 {
17529                     tag: 'input',
17530                     type : 'hidden',
17531                     cls: 'form-hidden-field'
17532                 },
17533                 ibwrap
17534             ]
17535         };
17536         
17537         if(!this.multiple && this.showToggleBtn){
17538             
17539             var caret = {
17540                 cls: 'caret'
17541             };
17542             
17543             if (this.caret != false) {
17544                 caret = {
17545                      tag: 'i',
17546                      cls: 'fa fa-' + this.caret
17547                 };
17548                 
17549             }
17550             
17551             combobox.cn.push({
17552                 tag :'span',
17553                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17554                 cn : [
17555                     Roo.bootstrap.version == 3 ? caret : '',
17556                     {
17557                         tag: 'span',
17558                         cls: 'combobox-clear',
17559                         cn  : [
17560                             {
17561                                 tag : 'i',
17562                                 cls: 'icon-remove'
17563                             }
17564                         ]
17565                     }
17566                 ]
17567
17568             })
17569         }
17570         
17571         if(this.multiple){
17572             combobox.cls += ' roo-select2-container-multi';
17573         }
17574         
17575         var align = this.labelAlign || this.parentLabelAlign();
17576         
17577         if (align ==='left' && this.fieldLabel.length) {
17578
17579             cfg.cn = [
17580                 {
17581                    tag : 'i',
17582                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17583                    tooltip : 'This field is required'
17584                 },
17585                 {
17586                     tag: 'label',
17587                     cls : 'control-label col-form-label',
17588                     html : this.fieldLabel
17589
17590                 },
17591                 {
17592                     cls : 'roo-combobox-wrap ', 
17593                     cn: [
17594                         combobox
17595                     ]
17596                 }
17597             ];
17598             
17599             var labelCfg = cfg.cn[1];
17600             var contentCfg = cfg.cn[2];
17601             
17602
17603             if(this.indicatorpos == 'right'){
17604                 cfg.cn = [
17605                     {
17606                         tag: 'label',
17607                         'for' :  id,
17608                         cls : 'control-label col-form-label',
17609                         cn : [
17610                             {
17611                                 tag : 'span',
17612                                 html : this.fieldLabel
17613                             },
17614                             {
17615                                 tag : 'i',
17616                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17617                                 tooltip : 'This field is required'
17618                             }
17619                         ]
17620                     },
17621                     {
17622                         cls : "roo-combobox-wrap ",
17623                         cn: [
17624                             combobox
17625                         ]
17626                     }
17627
17628                 ];
17629                 
17630                 labelCfg = cfg.cn[0];
17631                 contentCfg = cfg.cn[1];
17632             }
17633             
17634            
17635             
17636             if(this.labelWidth > 12){
17637                 labelCfg.style = "width: " + this.labelWidth + 'px';
17638             }
17639            
17640             if(this.labelWidth < 13 && this.labelmd == 0){
17641                 this.labelmd = this.labelWidth;
17642             }
17643             
17644             if(this.labellg > 0){
17645                 labelCfg.cls += ' col-lg-' + this.labellg;
17646                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17647             }
17648             
17649             if(this.labelmd > 0){
17650                 labelCfg.cls += ' col-md-' + this.labelmd;
17651                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17652             }
17653             
17654             if(this.labelsm > 0){
17655                 labelCfg.cls += ' col-sm-' + this.labelsm;
17656                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17657             }
17658             
17659             if(this.labelxs > 0){
17660                 labelCfg.cls += ' col-xs-' + this.labelxs;
17661                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17662             }
17663                 
17664                 
17665         } else if ( this.fieldLabel.length) {
17666             cfg.cn = [
17667                 {
17668                    tag : 'i',
17669                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17670                    tooltip : 'This field is required'
17671                 },
17672                 {
17673                     tag: 'label',
17674                     cls : 'control-label',
17675                     html : this.fieldLabel
17676
17677                 },
17678                 {
17679                     cls : '', 
17680                     cn: [
17681                         combobox
17682                     ]
17683                 }
17684             ];
17685             
17686             if(this.indicatorpos == 'right'){
17687                 cfg.cn = [
17688                     {
17689                         tag: 'label',
17690                         cls : 'control-label',
17691                         html : this.fieldLabel,
17692                         cn : [
17693                             {
17694                                tag : 'i',
17695                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17696                                tooltip : 'This field is required'
17697                             }
17698                         ]
17699                     },
17700                     {
17701                         cls : '', 
17702                         cn: [
17703                             combobox
17704                         ]
17705                     }
17706                 ];
17707             }
17708         } else {
17709             cfg.cn = combobox;    
17710         }
17711         
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         return cfg;
17722     },
17723     
17724     initTouchView : function()
17725     {
17726         this.renderTouchView();
17727         
17728         this.touchViewEl.on('scroll', function(){
17729             this.el.dom.scrollTop = 0;
17730         }, this);
17731         
17732         this.originalValue = this.getValue();
17733         
17734         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17735         
17736         this.inputEl().on("click", this.showTouchView, this);
17737         if (this.triggerEl) {
17738             this.triggerEl.on("click", this.showTouchView, this);
17739         }
17740         
17741         
17742         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17743         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17744         
17745         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17746         
17747         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17748         this.store.on('load', this.onTouchViewLoad, this);
17749         this.store.on('loadexception', this.onTouchViewLoadException, this);
17750         
17751         if(this.hiddenName){
17752             
17753             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17754             
17755             this.hiddenField.dom.value =
17756                 this.hiddenValue !== undefined ? this.hiddenValue :
17757                 this.value !== undefined ? this.value : '';
17758         
17759             this.el.dom.removeAttribute('name');
17760             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17761         }
17762         
17763         if(this.multiple){
17764             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17765             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17766         }
17767         
17768         if(this.removable && !this.multiple){
17769             var close = this.closeTriggerEl();
17770             if(close){
17771                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17772                 close.on('click', this.removeBtnClick, this, close);
17773             }
17774         }
17775         /*
17776          * fix the bug in Safari iOS8
17777          */
17778         this.inputEl().on("focus", function(e){
17779             document.activeElement.blur();
17780         }, this);
17781         
17782         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17783         
17784         return;
17785         
17786         
17787     },
17788     
17789     renderTouchView : function()
17790     {
17791         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17792         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17793         
17794         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17795         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17796         
17797         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17798         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17799         this.touchViewBodyEl.setStyle('overflow', 'auto');
17800         
17801         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17802         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17803         
17804         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17805         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17806         
17807     },
17808     
17809     showTouchView : function()
17810     {
17811         if(this.disabled){
17812             return;
17813         }
17814         
17815         this.touchViewHeaderEl.hide();
17816
17817         if(this.modalTitle.length){
17818             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17819             this.touchViewHeaderEl.show();
17820         }
17821
17822         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17823         this.touchViewEl.show();
17824
17825         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17826         
17827         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17828         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17829
17830         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17831
17832         if(this.modalTitle.length){
17833             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17834         }
17835         
17836         this.touchViewBodyEl.setHeight(bodyHeight);
17837
17838         if(this.animate){
17839             var _this = this;
17840             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17841         }else{
17842             this.touchViewEl.addClass(['in','show']);
17843         }
17844         
17845         if(this._touchViewMask){
17846             Roo.get(document.body).addClass("x-body-masked");
17847             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17848             this._touchViewMask.setStyle('z-index', 10000);
17849             this._touchViewMask.addClass('show');
17850         }
17851         
17852         this.doTouchViewQuery();
17853         
17854     },
17855     
17856     hideTouchView : function()
17857     {
17858         this.touchViewEl.removeClass(['in','show']);
17859
17860         if(this.animate){
17861             var _this = this;
17862             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17863         }else{
17864             this.touchViewEl.setStyle('display', 'none');
17865         }
17866         
17867         if(this._touchViewMask){
17868             this._touchViewMask.removeClass('show');
17869             Roo.get(document.body).removeClass("x-body-masked");
17870         }
17871     },
17872     
17873     setTouchViewValue : function()
17874     {
17875         if(this.multiple){
17876             this.clearItem();
17877         
17878             var _this = this;
17879
17880             Roo.each(this.tickItems, function(o){
17881                 this.addItem(o);
17882             }, this);
17883         }
17884         
17885         this.hideTouchView();
17886     },
17887     
17888     doTouchViewQuery : function()
17889     {
17890         var qe = {
17891             query: '',
17892             forceAll: true,
17893             combo: this,
17894             cancel:false
17895         };
17896         
17897         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17898             return false;
17899         }
17900         
17901         if(!this.alwaysQuery || this.mode == 'local'){
17902             this.onTouchViewLoad();
17903             return;
17904         }
17905         
17906         this.store.load();
17907     },
17908     
17909     onTouchViewBeforeLoad : function(combo,opts)
17910     {
17911         return;
17912     },
17913
17914     // private
17915     onTouchViewLoad : function()
17916     {
17917         if(this.store.getCount() < 1){
17918             this.onTouchViewEmptyResults();
17919             return;
17920         }
17921         
17922         this.clearTouchView();
17923         
17924         var rawValue = this.getRawValue();
17925         
17926         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17927         
17928         this.tickItems = [];
17929         
17930         this.store.data.each(function(d, rowIndex){
17931             var row = this.touchViewListGroup.createChild(template);
17932             
17933             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17934                 row.addClass(d.data.cls);
17935             }
17936             
17937             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17938                 var cfg = {
17939                     data : d.data,
17940                     html : d.data[this.displayField]
17941                 };
17942                 
17943                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17944                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17945                 }
17946             }
17947             row.removeClass('selected');
17948             if(!this.multiple && this.valueField &&
17949                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17950             {
17951                 // radio buttons..
17952                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17953                 row.addClass('selected');
17954             }
17955             
17956             if(this.multiple && this.valueField &&
17957                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17958             {
17959                 
17960                 // checkboxes...
17961                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17962                 this.tickItems.push(d.data);
17963             }
17964             
17965             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17966             
17967         }, this);
17968         
17969         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17970         
17971         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17972
17973         if(this.modalTitle.length){
17974             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17975         }
17976
17977         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17978         
17979         if(this.mobile_restrict_height && listHeight < bodyHeight){
17980             this.touchViewBodyEl.setHeight(listHeight);
17981         }
17982         
17983         var _this = this;
17984         
17985         if(firstChecked && listHeight > bodyHeight){
17986             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17987         }
17988         
17989     },
17990     
17991     onTouchViewLoadException : function()
17992     {
17993         this.hideTouchView();
17994     },
17995     
17996     onTouchViewEmptyResults : function()
17997     {
17998         this.clearTouchView();
17999         
18000         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18001         
18002         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18003         
18004     },
18005     
18006     clearTouchView : function()
18007     {
18008         this.touchViewListGroup.dom.innerHTML = '';
18009     },
18010     
18011     onTouchViewClick : function(e, el, o)
18012     {
18013         e.preventDefault();
18014         
18015         var row = o.row;
18016         var rowIndex = o.rowIndex;
18017         
18018         var r = this.store.getAt(rowIndex);
18019         
18020         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18021             
18022             if(!this.multiple){
18023                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18024                     c.dom.removeAttribute('checked');
18025                 }, this);
18026
18027                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18028
18029                 this.setFromData(r.data);
18030
18031                 var close = this.closeTriggerEl();
18032
18033                 if(close){
18034                     close.show();
18035                 }
18036
18037                 this.hideTouchView();
18038
18039                 this.fireEvent('select', this, r, rowIndex);
18040
18041                 return;
18042             }
18043
18044             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18045                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18046                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18047                 return;
18048             }
18049
18050             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18051             this.addItem(r.data);
18052             this.tickItems.push(r.data);
18053         }
18054     },
18055     
18056     getAutoCreateNativeIOS : function()
18057     {
18058         var cfg = {
18059             cls: 'form-group' //input-group,
18060         };
18061         
18062         var combobox =  {
18063             tag: 'select',
18064             cls : 'roo-ios-select'
18065         };
18066         
18067         if (this.name) {
18068             combobox.name = this.name;
18069         }
18070         
18071         if (this.disabled) {
18072             combobox.disabled = true;
18073         }
18074         
18075         var settings = this;
18076         
18077         ['xs','sm','md','lg'].map(function(size){
18078             if (settings[size]) {
18079                 cfg.cls += ' col-' + size + '-' + settings[size];
18080             }
18081         });
18082         
18083         cfg.cn = combobox;
18084         
18085         return cfg;
18086         
18087     },
18088     
18089     initIOSView : function()
18090     {
18091         this.store.on('load', this.onIOSViewLoad, this);
18092         
18093         return;
18094     },
18095     
18096     onIOSViewLoad : function()
18097     {
18098         if(this.store.getCount() < 1){
18099             return;
18100         }
18101         
18102         this.clearIOSView();
18103         
18104         if(this.allowBlank) {
18105             
18106             var default_text = '-- SELECT --';
18107             
18108             if(this.placeholder.length){
18109                 default_text = this.placeholder;
18110             }
18111             
18112             if(this.emptyTitle.length){
18113                 default_text += ' - ' + this.emptyTitle + ' -';
18114             }
18115             
18116             var opt = this.inputEl().createChild({
18117                 tag: 'option',
18118                 value : 0,
18119                 html : default_text
18120             });
18121             
18122             var o = {};
18123             o[this.valueField] = 0;
18124             o[this.displayField] = default_text;
18125             
18126             this.ios_options.push({
18127                 data : o,
18128                 el : opt
18129             });
18130             
18131         }
18132         
18133         this.store.data.each(function(d, rowIndex){
18134             
18135             var html = '';
18136             
18137             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18138                 html = d.data[this.displayField];
18139             }
18140             
18141             var value = '';
18142             
18143             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18144                 value = d.data[this.valueField];
18145             }
18146             
18147             var option = {
18148                 tag: 'option',
18149                 value : value,
18150                 html : html
18151             };
18152             
18153             if(this.value == d.data[this.valueField]){
18154                 option['selected'] = true;
18155             }
18156             
18157             var opt = this.inputEl().createChild(option);
18158             
18159             this.ios_options.push({
18160                 data : d.data,
18161                 el : opt
18162             });
18163             
18164         }, this);
18165         
18166         this.inputEl().on('change', function(){
18167            this.fireEvent('select', this);
18168         }, this);
18169         
18170     },
18171     
18172     clearIOSView: function()
18173     {
18174         this.inputEl().dom.innerHTML = '';
18175         
18176         this.ios_options = [];
18177     },
18178     
18179     setIOSValue: function(v)
18180     {
18181         this.value = v;
18182         
18183         if(!this.ios_options){
18184             return;
18185         }
18186         
18187         Roo.each(this.ios_options, function(opts){
18188            
18189            opts.el.dom.removeAttribute('selected');
18190            
18191            if(opts.data[this.valueField] != v){
18192                return;
18193            }
18194            
18195            opts.el.dom.setAttribute('selected', true);
18196            
18197         }, this);
18198     }
18199
18200     /** 
18201     * @cfg {Boolean} grow 
18202     * @hide 
18203     */
18204     /** 
18205     * @cfg {Number} growMin 
18206     * @hide 
18207     */
18208     /** 
18209     * @cfg {Number} growMax 
18210     * @hide 
18211     */
18212     /**
18213      * @hide
18214      * @method autoSize
18215      */
18216 });
18217
18218 Roo.apply(Roo.bootstrap.ComboBox,  {
18219     
18220     header : {
18221         tag: 'div',
18222         cls: 'modal-header',
18223         cn: [
18224             {
18225                 tag: 'h4',
18226                 cls: 'modal-title'
18227             }
18228         ]
18229     },
18230     
18231     body : {
18232         tag: 'div',
18233         cls: 'modal-body',
18234         cn: [
18235             {
18236                 tag: 'ul',
18237                 cls: 'list-group'
18238             }
18239         ]
18240     },
18241     
18242     listItemRadio : {
18243         tag: 'li',
18244         cls: 'list-group-item',
18245         cn: [
18246             {
18247                 tag: 'span',
18248                 cls: 'roo-combobox-list-group-item-value'
18249             },
18250             {
18251                 tag: 'div',
18252                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18253                 cn: [
18254                     {
18255                         tag: 'input',
18256                         type: 'radio'
18257                     },
18258                     {
18259                         tag: 'label'
18260                     }
18261                 ]
18262             }
18263         ]
18264     },
18265     
18266     listItemCheckbox : {
18267         tag: 'li',
18268         cls: 'list-group-item',
18269         cn: [
18270             {
18271                 tag: 'span',
18272                 cls: 'roo-combobox-list-group-item-value'
18273             },
18274             {
18275                 tag: 'div',
18276                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18277                 cn: [
18278                     {
18279                         tag: 'input',
18280                         type: 'checkbox'
18281                     },
18282                     {
18283                         tag: 'label'
18284                     }
18285                 ]
18286             }
18287         ]
18288     },
18289     
18290     emptyResult : {
18291         tag: 'div',
18292         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18293     },
18294     
18295     footer : {
18296         tag: 'div',
18297         cls: 'modal-footer',
18298         cn: [
18299             {
18300                 tag: 'div',
18301                 cls: 'row',
18302                 cn: [
18303                     {
18304                         tag: 'div',
18305                         cls: 'col-xs-6 text-left',
18306                         cn: {
18307                             tag: 'button',
18308                             cls: 'btn btn-danger roo-touch-view-cancel',
18309                             html: 'Cancel'
18310                         }
18311                     },
18312                     {
18313                         tag: 'div',
18314                         cls: 'col-xs-6 text-right',
18315                         cn: {
18316                             tag: 'button',
18317                             cls: 'btn btn-success roo-touch-view-ok',
18318                             html: 'OK'
18319                         }
18320                     }
18321                 ]
18322             }
18323         ]
18324         
18325     }
18326 });
18327
18328 Roo.apply(Roo.bootstrap.ComboBox,  {
18329     
18330     touchViewTemplate : {
18331         tag: 'div',
18332         cls: 'modal fade roo-combobox-touch-view',
18333         cn: [
18334             {
18335                 tag: 'div',
18336                 cls: 'modal-dialog',
18337                 style : 'position:fixed', // we have to fix position....
18338                 cn: [
18339                     {
18340                         tag: 'div',
18341                         cls: 'modal-content',
18342                         cn: [
18343                             Roo.bootstrap.ComboBox.header,
18344                             Roo.bootstrap.ComboBox.body,
18345                             Roo.bootstrap.ComboBox.footer
18346                         ]
18347                     }
18348                 ]
18349             }
18350         ]
18351     }
18352 });/*
18353  * Based on:
18354  * Ext JS Library 1.1.1
18355  * Copyright(c) 2006-2007, Ext JS, LLC.
18356  *
18357  * Originally Released Under LGPL - original licence link has changed is not relivant.
18358  *
18359  * Fork - LGPL
18360  * <script type="text/javascript">
18361  */
18362
18363 /**
18364  * @class Roo.View
18365  * @extends Roo.util.Observable
18366  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18367  * This class also supports single and multi selection modes. <br>
18368  * Create a data model bound view:
18369  <pre><code>
18370  var store = new Roo.data.Store(...);
18371
18372  var view = new Roo.View({
18373     el : "my-element",
18374     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18375  
18376     singleSelect: true,
18377     selectedClass: "ydataview-selected",
18378     store: store
18379  });
18380
18381  // listen for node click?
18382  view.on("click", function(vw, index, node, e){
18383  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18384  });
18385
18386  // load XML data
18387  dataModel.load("foobar.xml");
18388  </code></pre>
18389  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18390  * <br><br>
18391  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18392  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18393  * 
18394  * Note: old style constructor is still suported (container, template, config)
18395  * 
18396  * @constructor
18397  * Create a new View
18398  * @param {Object} config The config object
18399  * 
18400  */
18401 Roo.View = function(config, depreciated_tpl, depreciated_config){
18402     
18403     this.parent = false;
18404     
18405     if (typeof(depreciated_tpl) == 'undefined') {
18406         // new way.. - universal constructor.
18407         Roo.apply(this, config);
18408         this.el  = Roo.get(this.el);
18409     } else {
18410         // old format..
18411         this.el  = Roo.get(config);
18412         this.tpl = depreciated_tpl;
18413         Roo.apply(this, depreciated_config);
18414     }
18415     this.wrapEl  = this.el.wrap().wrap();
18416     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18417     
18418     
18419     if(typeof(this.tpl) == "string"){
18420         this.tpl = new Roo.Template(this.tpl);
18421     } else {
18422         // support xtype ctors..
18423         this.tpl = new Roo.factory(this.tpl, Roo);
18424     }
18425     
18426     
18427     this.tpl.compile();
18428     
18429     /** @private */
18430     this.addEvents({
18431         /**
18432          * @event beforeclick
18433          * Fires before a click is processed. Returns false to cancel the default action.
18434          * @param {Roo.View} this
18435          * @param {Number} index The index of the target node
18436          * @param {HTMLElement} node The target node
18437          * @param {Roo.EventObject} e The raw event object
18438          */
18439             "beforeclick" : true,
18440         /**
18441          * @event click
18442          * Fires when a template node is clicked.
18443          * @param {Roo.View} this
18444          * @param {Number} index The index of the target node
18445          * @param {HTMLElement} node The target node
18446          * @param {Roo.EventObject} e The raw event object
18447          */
18448             "click" : true,
18449         /**
18450          * @event dblclick
18451          * Fires when a template node is double clicked.
18452          * @param {Roo.View} this
18453          * @param {Number} index The index of the target node
18454          * @param {HTMLElement} node The target node
18455          * @param {Roo.EventObject} e The raw event object
18456          */
18457             "dblclick" : true,
18458         /**
18459          * @event contextmenu
18460          * Fires when a template node is right clicked.
18461          * @param {Roo.View} this
18462          * @param {Number} index The index of the target node
18463          * @param {HTMLElement} node The target node
18464          * @param {Roo.EventObject} e The raw event object
18465          */
18466             "contextmenu" : true,
18467         /**
18468          * @event selectionchange
18469          * Fires when the selected nodes change.
18470          * @param {Roo.View} this
18471          * @param {Array} selections Array of the selected nodes
18472          */
18473             "selectionchange" : true,
18474     
18475         /**
18476          * @event beforeselect
18477          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18478          * @param {Roo.View} this
18479          * @param {HTMLElement} node The node to be selected
18480          * @param {Array} selections Array of currently selected nodes
18481          */
18482             "beforeselect" : true,
18483         /**
18484          * @event preparedata
18485          * Fires on every row to render, to allow you to change the data.
18486          * @param {Roo.View} this
18487          * @param {Object} data to be rendered (change this)
18488          */
18489           "preparedata" : true
18490           
18491           
18492         });
18493
18494
18495
18496     this.el.on({
18497         "click": this.onClick,
18498         "dblclick": this.onDblClick,
18499         "contextmenu": this.onContextMenu,
18500         scope:this
18501     });
18502
18503     this.selections = [];
18504     this.nodes = [];
18505     this.cmp = new Roo.CompositeElementLite([]);
18506     if(this.store){
18507         this.store = Roo.factory(this.store, Roo.data);
18508         this.setStore(this.store, true);
18509     }
18510     
18511     if ( this.footer && this.footer.xtype) {
18512            
18513          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18514         
18515         this.footer.dataSource = this.store;
18516         this.footer.container = fctr;
18517         this.footer = Roo.factory(this.footer, Roo);
18518         fctr.insertFirst(this.el);
18519         
18520         // this is a bit insane - as the paging toolbar seems to detach the el..
18521 //        dom.parentNode.parentNode.parentNode
18522          // they get detached?
18523     }
18524     
18525     
18526     Roo.View.superclass.constructor.call(this);
18527     
18528     
18529 };
18530
18531 Roo.extend(Roo.View, Roo.util.Observable, {
18532     
18533      /**
18534      * @cfg {Roo.data.Store} store Data store to load data from.
18535      */
18536     store : false,
18537     
18538     /**
18539      * @cfg {String|Roo.Element} el The container element.
18540      */
18541     el : '',
18542     
18543     /**
18544      * @cfg {String|Roo.Template} tpl The template used by this View 
18545      */
18546     tpl : false,
18547     /**
18548      * @cfg {String} dataName the named area of the template to use as the data area
18549      *                          Works with domtemplates roo-name="name"
18550      */
18551     dataName: false,
18552     /**
18553      * @cfg {String} selectedClass The css class to add to selected nodes
18554      */
18555     selectedClass : "x-view-selected",
18556      /**
18557      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18558      */
18559     emptyText : "",
18560     
18561     /**
18562      * @cfg {String} text to display on mask (default Loading)
18563      */
18564     mask : false,
18565     /**
18566      * @cfg {Boolean} multiSelect Allow multiple selection
18567      */
18568     multiSelect : false,
18569     /**
18570      * @cfg {Boolean} singleSelect Allow single selection
18571      */
18572     singleSelect:  false,
18573     
18574     /**
18575      * @cfg {Boolean} toggleSelect - selecting 
18576      */
18577     toggleSelect : false,
18578     
18579     /**
18580      * @cfg {Boolean} tickable - selecting 
18581      */
18582     tickable : false,
18583     
18584     /**
18585      * Returns the element this view is bound to.
18586      * @return {Roo.Element}
18587      */
18588     getEl : function(){
18589         return this.wrapEl;
18590     },
18591     
18592     
18593
18594     /**
18595      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18596      */
18597     refresh : function(){
18598         //Roo.log('refresh');
18599         var t = this.tpl;
18600         
18601         // if we are using something like 'domtemplate', then
18602         // the what gets used is:
18603         // t.applySubtemplate(NAME, data, wrapping data..)
18604         // the outer template then get' applied with
18605         //     the store 'extra data'
18606         // and the body get's added to the
18607         //      roo-name="data" node?
18608         //      <span class='roo-tpl-{name}'></span> ?????
18609         
18610         
18611         
18612         this.clearSelections();
18613         this.el.update("");
18614         var html = [];
18615         var records = this.store.getRange();
18616         if(records.length < 1) {
18617             
18618             // is this valid??  = should it render a template??
18619             
18620             this.el.update(this.emptyText);
18621             return;
18622         }
18623         var el = this.el;
18624         if (this.dataName) {
18625             this.el.update(t.apply(this.store.meta)); //????
18626             el = this.el.child('.roo-tpl-' + this.dataName);
18627         }
18628         
18629         for(var i = 0, len = records.length; i < len; i++){
18630             var data = this.prepareData(records[i].data, i, records[i]);
18631             this.fireEvent("preparedata", this, data, i, records[i]);
18632             
18633             var d = Roo.apply({}, data);
18634             
18635             if(this.tickable){
18636                 Roo.apply(d, {'roo-id' : Roo.id()});
18637                 
18638                 var _this = this;
18639             
18640                 Roo.each(this.parent.item, function(item){
18641                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18642                         return;
18643                     }
18644                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18645                 });
18646             }
18647             
18648             html[html.length] = Roo.util.Format.trim(
18649                 this.dataName ?
18650                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18651                     t.apply(d)
18652             );
18653         }
18654         
18655         
18656         
18657         el.update(html.join(""));
18658         this.nodes = el.dom.childNodes;
18659         this.updateIndexes(0);
18660     },
18661     
18662
18663     /**
18664      * Function to override to reformat the data that is sent to
18665      * the template for each node.
18666      * DEPRICATED - use the preparedata event handler.
18667      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18668      * a JSON object for an UpdateManager bound view).
18669      */
18670     prepareData : function(data, index, record)
18671     {
18672         this.fireEvent("preparedata", this, data, index, record);
18673         return data;
18674     },
18675
18676     onUpdate : function(ds, record){
18677         // Roo.log('on update');   
18678         this.clearSelections();
18679         var index = this.store.indexOf(record);
18680         var n = this.nodes[index];
18681         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18682         n.parentNode.removeChild(n);
18683         this.updateIndexes(index, index);
18684     },
18685
18686     
18687     
18688 // --------- FIXME     
18689     onAdd : function(ds, records, index)
18690     {
18691         //Roo.log(['on Add', ds, records, index] );        
18692         this.clearSelections();
18693         if(this.nodes.length == 0){
18694             this.refresh();
18695             return;
18696         }
18697         var n = this.nodes[index];
18698         for(var i = 0, len = records.length; i < len; i++){
18699             var d = this.prepareData(records[i].data, i, records[i]);
18700             if(n){
18701                 this.tpl.insertBefore(n, d);
18702             }else{
18703                 
18704                 this.tpl.append(this.el, d);
18705             }
18706         }
18707         this.updateIndexes(index);
18708     },
18709
18710     onRemove : function(ds, record, index){
18711        // Roo.log('onRemove');
18712         this.clearSelections();
18713         var el = this.dataName  ?
18714             this.el.child('.roo-tpl-' + this.dataName) :
18715             this.el; 
18716         
18717         el.dom.removeChild(this.nodes[index]);
18718         this.updateIndexes(index);
18719     },
18720
18721     /**
18722      * Refresh an individual node.
18723      * @param {Number} index
18724      */
18725     refreshNode : function(index){
18726         this.onUpdate(this.store, this.store.getAt(index));
18727     },
18728
18729     updateIndexes : function(startIndex, endIndex){
18730         var ns = this.nodes;
18731         startIndex = startIndex || 0;
18732         endIndex = endIndex || ns.length - 1;
18733         for(var i = startIndex; i <= endIndex; i++){
18734             ns[i].nodeIndex = i;
18735         }
18736     },
18737
18738     /**
18739      * Changes the data store this view uses and refresh the view.
18740      * @param {Store} store
18741      */
18742     setStore : function(store, initial){
18743         if(!initial && this.store){
18744             this.store.un("datachanged", this.refresh);
18745             this.store.un("add", this.onAdd);
18746             this.store.un("remove", this.onRemove);
18747             this.store.un("update", this.onUpdate);
18748             this.store.un("clear", this.refresh);
18749             this.store.un("beforeload", this.onBeforeLoad);
18750             this.store.un("load", this.onLoad);
18751             this.store.un("loadexception", this.onLoad);
18752         }
18753         if(store){
18754           
18755             store.on("datachanged", this.refresh, this);
18756             store.on("add", this.onAdd, this);
18757             store.on("remove", this.onRemove, this);
18758             store.on("update", this.onUpdate, this);
18759             store.on("clear", this.refresh, this);
18760             store.on("beforeload", this.onBeforeLoad, this);
18761             store.on("load", this.onLoad, this);
18762             store.on("loadexception", this.onLoad, this);
18763         }
18764         
18765         if(store){
18766             this.refresh();
18767         }
18768     },
18769     /**
18770      * onbeforeLoad - masks the loading area.
18771      *
18772      */
18773     onBeforeLoad : function(store,opts)
18774     {
18775          //Roo.log('onBeforeLoad');   
18776         if (!opts.add) {
18777             this.el.update("");
18778         }
18779         this.el.mask(this.mask ? this.mask : "Loading" ); 
18780     },
18781     onLoad : function ()
18782     {
18783         this.el.unmask();
18784     },
18785     
18786
18787     /**
18788      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18789      * @param {HTMLElement} node
18790      * @return {HTMLElement} The template node
18791      */
18792     findItemFromChild : function(node){
18793         var el = this.dataName  ?
18794             this.el.child('.roo-tpl-' + this.dataName,true) :
18795             this.el.dom; 
18796         
18797         if(!node || node.parentNode == el){
18798                     return node;
18799             }
18800             var p = node.parentNode;
18801             while(p && p != el){
18802             if(p.parentNode == el){
18803                 return p;
18804             }
18805             p = p.parentNode;
18806         }
18807             return null;
18808     },
18809
18810     /** @ignore */
18811     onClick : function(e){
18812         var item = this.findItemFromChild(e.getTarget());
18813         if(item){
18814             var index = this.indexOf(item);
18815             if(this.onItemClick(item, index, e) !== false){
18816                 this.fireEvent("click", this, index, item, e);
18817             }
18818         }else{
18819             this.clearSelections();
18820         }
18821     },
18822
18823     /** @ignore */
18824     onContextMenu : function(e){
18825         var item = this.findItemFromChild(e.getTarget());
18826         if(item){
18827             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18828         }
18829     },
18830
18831     /** @ignore */
18832     onDblClick : function(e){
18833         var item = this.findItemFromChild(e.getTarget());
18834         if(item){
18835             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18836         }
18837     },
18838
18839     onItemClick : function(item, index, e)
18840     {
18841         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18842             return false;
18843         }
18844         if (this.toggleSelect) {
18845             var m = this.isSelected(item) ? 'unselect' : 'select';
18846             //Roo.log(m);
18847             var _t = this;
18848             _t[m](item, true, false);
18849             return true;
18850         }
18851         if(this.multiSelect || this.singleSelect){
18852             if(this.multiSelect && e.shiftKey && this.lastSelection){
18853                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18854             }else{
18855                 this.select(item, this.multiSelect && e.ctrlKey);
18856                 this.lastSelection = item;
18857             }
18858             
18859             if(!this.tickable){
18860                 e.preventDefault();
18861             }
18862             
18863         }
18864         return true;
18865     },
18866
18867     /**
18868      * Get the number of selected nodes.
18869      * @return {Number}
18870      */
18871     getSelectionCount : function(){
18872         return this.selections.length;
18873     },
18874
18875     /**
18876      * Get the currently selected nodes.
18877      * @return {Array} An array of HTMLElements
18878      */
18879     getSelectedNodes : function(){
18880         return this.selections;
18881     },
18882
18883     /**
18884      * Get the indexes of the selected nodes.
18885      * @return {Array}
18886      */
18887     getSelectedIndexes : function(){
18888         var indexes = [], s = this.selections;
18889         for(var i = 0, len = s.length; i < len; i++){
18890             indexes.push(s[i].nodeIndex);
18891         }
18892         return indexes;
18893     },
18894
18895     /**
18896      * Clear all selections
18897      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18898      */
18899     clearSelections : function(suppressEvent){
18900         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18901             this.cmp.elements = this.selections;
18902             this.cmp.removeClass(this.selectedClass);
18903             this.selections = [];
18904             if(!suppressEvent){
18905                 this.fireEvent("selectionchange", this, this.selections);
18906             }
18907         }
18908     },
18909
18910     /**
18911      * Returns true if the passed node is selected
18912      * @param {HTMLElement/Number} node The node or node index
18913      * @return {Boolean}
18914      */
18915     isSelected : function(node){
18916         var s = this.selections;
18917         if(s.length < 1){
18918             return false;
18919         }
18920         node = this.getNode(node);
18921         return s.indexOf(node) !== -1;
18922     },
18923
18924     /**
18925      * Selects nodes.
18926      * @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
18927      * @param {Boolean} keepExisting (optional) true to keep existing selections
18928      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18929      */
18930     select : function(nodeInfo, keepExisting, suppressEvent){
18931         if(nodeInfo instanceof Array){
18932             if(!keepExisting){
18933                 this.clearSelections(true);
18934             }
18935             for(var i = 0, len = nodeInfo.length; i < len; i++){
18936                 this.select(nodeInfo[i], true, true);
18937             }
18938             return;
18939         } 
18940         var node = this.getNode(nodeInfo);
18941         if(!node || this.isSelected(node)){
18942             return; // already selected.
18943         }
18944         if(!keepExisting){
18945             this.clearSelections(true);
18946         }
18947         
18948         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18949             Roo.fly(node).addClass(this.selectedClass);
18950             this.selections.push(node);
18951             if(!suppressEvent){
18952                 this.fireEvent("selectionchange", this, this.selections);
18953             }
18954         }
18955         
18956         
18957     },
18958       /**
18959      * Unselects nodes.
18960      * @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
18961      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18962      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18963      */
18964     unselect : function(nodeInfo, keepExisting, suppressEvent)
18965     {
18966         if(nodeInfo instanceof Array){
18967             Roo.each(this.selections, function(s) {
18968                 this.unselect(s, nodeInfo);
18969             }, this);
18970             return;
18971         }
18972         var node = this.getNode(nodeInfo);
18973         if(!node || !this.isSelected(node)){
18974             //Roo.log("not selected");
18975             return; // not selected.
18976         }
18977         // fireevent???
18978         var ns = [];
18979         Roo.each(this.selections, function(s) {
18980             if (s == node ) {
18981                 Roo.fly(node).removeClass(this.selectedClass);
18982
18983                 return;
18984             }
18985             ns.push(s);
18986         },this);
18987         
18988         this.selections= ns;
18989         this.fireEvent("selectionchange", this, this.selections);
18990     },
18991
18992     /**
18993      * Gets a template node.
18994      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18995      * @return {HTMLElement} The node or null if it wasn't found
18996      */
18997     getNode : function(nodeInfo){
18998         if(typeof nodeInfo == "string"){
18999             return document.getElementById(nodeInfo);
19000         }else if(typeof nodeInfo == "number"){
19001             return this.nodes[nodeInfo];
19002         }
19003         return nodeInfo;
19004     },
19005
19006     /**
19007      * Gets a range template nodes.
19008      * @param {Number} startIndex
19009      * @param {Number} endIndex
19010      * @return {Array} An array of nodes
19011      */
19012     getNodes : function(start, end){
19013         var ns = this.nodes;
19014         start = start || 0;
19015         end = typeof end == "undefined" ? ns.length - 1 : end;
19016         var nodes = [];
19017         if(start <= end){
19018             for(var i = start; i <= end; i++){
19019                 nodes.push(ns[i]);
19020             }
19021         } else{
19022             for(var i = start; i >= end; i--){
19023                 nodes.push(ns[i]);
19024             }
19025         }
19026         return nodes;
19027     },
19028
19029     /**
19030      * Finds the index of the passed node
19031      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19032      * @return {Number} The index of the node or -1
19033      */
19034     indexOf : function(node){
19035         node = this.getNode(node);
19036         if(typeof node.nodeIndex == "number"){
19037             return node.nodeIndex;
19038         }
19039         var ns = this.nodes;
19040         for(var i = 0, len = ns.length; i < len; i++){
19041             if(ns[i] == node){
19042                 return i;
19043             }
19044         }
19045         return -1;
19046     }
19047 });
19048 /*
19049  * - LGPL
19050  *
19051  * based on jquery fullcalendar
19052  * 
19053  */
19054
19055 Roo.bootstrap = Roo.bootstrap || {};
19056 /**
19057  * @class Roo.bootstrap.Calendar
19058  * @extends Roo.bootstrap.Component
19059  * Bootstrap Calendar class
19060  * @cfg {Boolean} loadMask (true|false) default false
19061  * @cfg {Object} header generate the user specific header of the calendar, default false
19062
19063  * @constructor
19064  * Create a new Container
19065  * @param {Object} config The config object
19066  */
19067
19068
19069
19070 Roo.bootstrap.Calendar = function(config){
19071     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19072      this.addEvents({
19073         /**
19074              * @event select
19075              * Fires when a date is selected
19076              * @param {DatePicker} this
19077              * @param {Date} date The selected date
19078              */
19079         'select': true,
19080         /**
19081              * @event monthchange
19082              * Fires when the displayed month changes 
19083              * @param {DatePicker} this
19084              * @param {Date} date The selected month
19085              */
19086         'monthchange': true,
19087         /**
19088              * @event evententer
19089              * Fires when mouse over an event
19090              * @param {Calendar} this
19091              * @param {event} Event
19092              */
19093         'evententer': true,
19094         /**
19095              * @event eventleave
19096              * Fires when the mouse leaves an
19097              * @param {Calendar} this
19098              * @param {event}
19099              */
19100         'eventleave': true,
19101         /**
19102              * @event eventclick
19103              * Fires when the mouse click an
19104              * @param {Calendar} this
19105              * @param {event}
19106              */
19107         'eventclick': true
19108         
19109     });
19110
19111 };
19112
19113 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19114     
19115      /**
19116      * @cfg {Number} startDay
19117      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19118      */
19119     startDay : 0,
19120     
19121     loadMask : false,
19122     
19123     header : false,
19124       
19125     getAutoCreate : function(){
19126         
19127         
19128         var fc_button = function(name, corner, style, content ) {
19129             return Roo.apply({},{
19130                 tag : 'span',
19131                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19132                          (corner.length ?
19133                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19134                             ''
19135                         ),
19136                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19137                 unselectable: 'on'
19138             });
19139         };
19140         
19141         var header = {};
19142         
19143         if(!this.header){
19144             header = {
19145                 tag : 'table',
19146                 cls : 'fc-header',
19147                 style : 'width:100%',
19148                 cn : [
19149                     {
19150                         tag: 'tr',
19151                         cn : [
19152                             {
19153                                 tag : 'td',
19154                                 cls : 'fc-header-left',
19155                                 cn : [
19156                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19157                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19158                                     { tag: 'span', cls: 'fc-header-space' },
19159                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19160
19161
19162                                 ]
19163                             },
19164
19165                             {
19166                                 tag : 'td',
19167                                 cls : 'fc-header-center',
19168                                 cn : [
19169                                     {
19170                                         tag: 'span',
19171                                         cls: 'fc-header-title',
19172                                         cn : {
19173                                             tag: 'H2',
19174                                             html : 'month / year'
19175                                         }
19176                                     }
19177
19178                                 ]
19179                             },
19180                             {
19181                                 tag : 'td',
19182                                 cls : 'fc-header-right',
19183                                 cn : [
19184                               /*      fc_button('month', 'left', '', 'month' ),
19185                                     fc_button('week', '', '', 'week' ),
19186                                     fc_button('day', 'right', '', 'day' )
19187                                 */    
19188
19189                                 ]
19190                             }
19191
19192                         ]
19193                     }
19194                 ]
19195             };
19196         }
19197         
19198         header = this.header;
19199         
19200        
19201         var cal_heads = function() {
19202             var ret = [];
19203             // fixme - handle this.
19204             
19205             for (var i =0; i < Date.dayNames.length; i++) {
19206                 var d = Date.dayNames[i];
19207                 ret.push({
19208                     tag: 'th',
19209                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19210                     html : d.substring(0,3)
19211                 });
19212                 
19213             }
19214             ret[0].cls += ' fc-first';
19215             ret[6].cls += ' fc-last';
19216             return ret;
19217         };
19218         var cal_cell = function(n) {
19219             return  {
19220                 tag: 'td',
19221                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19222                 cn : [
19223                     {
19224                         cn : [
19225                             {
19226                                 cls: 'fc-day-number',
19227                                 html: 'D'
19228                             },
19229                             {
19230                                 cls: 'fc-day-content',
19231                              
19232                                 cn : [
19233                                      {
19234                                         style: 'position: relative;' // height: 17px;
19235                                     }
19236                                 ]
19237                             }
19238                             
19239                             
19240                         ]
19241                     }
19242                 ]
19243                 
19244             }
19245         };
19246         var cal_rows = function() {
19247             
19248             var ret = [];
19249             for (var r = 0; r < 6; r++) {
19250                 var row= {
19251                     tag : 'tr',
19252                     cls : 'fc-week',
19253                     cn : []
19254                 };
19255                 
19256                 for (var i =0; i < Date.dayNames.length; i++) {
19257                     var d = Date.dayNames[i];
19258                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19259
19260                 }
19261                 row.cn[0].cls+=' fc-first';
19262                 row.cn[0].cn[0].style = 'min-height:90px';
19263                 row.cn[6].cls+=' fc-last';
19264                 ret.push(row);
19265                 
19266             }
19267             ret[0].cls += ' fc-first';
19268             ret[4].cls += ' fc-prev-last';
19269             ret[5].cls += ' fc-last';
19270             return ret;
19271             
19272         };
19273         
19274         var cal_table = {
19275             tag: 'table',
19276             cls: 'fc-border-separate',
19277             style : 'width:100%',
19278             cellspacing  : 0,
19279             cn : [
19280                 { 
19281                     tag: 'thead',
19282                     cn : [
19283                         { 
19284                             tag: 'tr',
19285                             cls : 'fc-first fc-last',
19286                             cn : cal_heads()
19287                         }
19288                     ]
19289                 },
19290                 { 
19291                     tag: 'tbody',
19292                     cn : cal_rows()
19293                 }
19294                   
19295             ]
19296         };
19297          
19298          var cfg = {
19299             cls : 'fc fc-ltr',
19300             cn : [
19301                 header,
19302                 {
19303                     cls : 'fc-content',
19304                     style : "position: relative;",
19305                     cn : [
19306                         {
19307                             cls : 'fc-view fc-view-month fc-grid',
19308                             style : 'position: relative',
19309                             unselectable : 'on',
19310                             cn : [
19311                                 {
19312                                     cls : 'fc-event-container',
19313                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19314                                 },
19315                                 cal_table
19316                             ]
19317                         }
19318                     ]
19319     
19320                 }
19321            ] 
19322             
19323         };
19324         
19325          
19326         
19327         return cfg;
19328     },
19329     
19330     
19331     initEvents : function()
19332     {
19333         if(!this.store){
19334             throw "can not find store for calendar";
19335         }
19336         
19337         var mark = {
19338             tag: "div",
19339             cls:"x-dlg-mask",
19340             style: "text-align:center",
19341             cn: [
19342                 {
19343                     tag: "div",
19344                     style: "background-color:white;width:50%;margin:250 auto",
19345                     cn: [
19346                         {
19347                             tag: "img",
19348                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19349                         },
19350                         {
19351                             tag: "span",
19352                             html: "Loading"
19353                         }
19354                         
19355                     ]
19356                 }
19357             ]
19358         };
19359         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19360         
19361         var size = this.el.select('.fc-content', true).first().getSize();
19362         this.maskEl.setSize(size.width, size.height);
19363         this.maskEl.enableDisplayMode("block");
19364         if(!this.loadMask){
19365             this.maskEl.hide();
19366         }
19367         
19368         this.store = Roo.factory(this.store, Roo.data);
19369         this.store.on('load', this.onLoad, this);
19370         this.store.on('beforeload', this.onBeforeLoad, this);
19371         
19372         this.resize();
19373         
19374         this.cells = this.el.select('.fc-day',true);
19375         //Roo.log(this.cells);
19376         this.textNodes = this.el.query('.fc-day-number');
19377         this.cells.addClassOnOver('fc-state-hover');
19378         
19379         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19380         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19381         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19382         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19383         
19384         this.on('monthchange', this.onMonthChange, this);
19385         
19386         this.update(new Date().clearTime());
19387     },
19388     
19389     resize : function() {
19390         var sz  = this.el.getSize();
19391         
19392         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19393         this.el.select('.fc-day-content div',true).setHeight(34);
19394     },
19395     
19396     
19397     // private
19398     showPrevMonth : function(e){
19399         this.update(this.activeDate.add("mo", -1));
19400     },
19401     showToday : function(e){
19402         this.update(new Date().clearTime());
19403     },
19404     // private
19405     showNextMonth : function(e){
19406         this.update(this.activeDate.add("mo", 1));
19407     },
19408
19409     // private
19410     showPrevYear : function(){
19411         this.update(this.activeDate.add("y", -1));
19412     },
19413
19414     // private
19415     showNextYear : function(){
19416         this.update(this.activeDate.add("y", 1));
19417     },
19418
19419     
19420    // private
19421     update : function(date)
19422     {
19423         var vd = this.activeDate;
19424         this.activeDate = date;
19425 //        if(vd && this.el){
19426 //            var t = date.getTime();
19427 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19428 //                Roo.log('using add remove');
19429 //                
19430 //                this.fireEvent('monthchange', this, date);
19431 //                
19432 //                this.cells.removeClass("fc-state-highlight");
19433 //                this.cells.each(function(c){
19434 //                   if(c.dateValue == t){
19435 //                       c.addClass("fc-state-highlight");
19436 //                       setTimeout(function(){
19437 //                            try{c.dom.firstChild.focus();}catch(e){}
19438 //                       }, 50);
19439 //                       return false;
19440 //                   }
19441 //                   return true;
19442 //                });
19443 //                return;
19444 //            }
19445 //        }
19446         
19447         var days = date.getDaysInMonth();
19448         
19449         var firstOfMonth = date.getFirstDateOfMonth();
19450         var startingPos = firstOfMonth.getDay()-this.startDay;
19451         
19452         if(startingPos < this.startDay){
19453             startingPos += 7;
19454         }
19455         
19456         var pm = date.add(Date.MONTH, -1);
19457         var prevStart = pm.getDaysInMonth()-startingPos;
19458 //        
19459         this.cells = this.el.select('.fc-day',true);
19460         this.textNodes = this.el.query('.fc-day-number');
19461         this.cells.addClassOnOver('fc-state-hover');
19462         
19463         var cells = this.cells.elements;
19464         var textEls = this.textNodes;
19465         
19466         Roo.each(cells, function(cell){
19467             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19468         });
19469         
19470         days += startingPos;
19471
19472         // convert everything to numbers so it's fast
19473         var day = 86400000;
19474         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19475         //Roo.log(d);
19476         //Roo.log(pm);
19477         //Roo.log(prevStart);
19478         
19479         var today = new Date().clearTime().getTime();
19480         var sel = date.clearTime().getTime();
19481         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19482         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19483         var ddMatch = this.disabledDatesRE;
19484         var ddText = this.disabledDatesText;
19485         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19486         var ddaysText = this.disabledDaysText;
19487         var format = this.format;
19488         
19489         var setCellClass = function(cal, cell){
19490             cell.row = 0;
19491             cell.events = [];
19492             cell.more = [];
19493             //Roo.log('set Cell Class');
19494             cell.title = "";
19495             var t = d.getTime();
19496             
19497             //Roo.log(d);
19498             
19499             cell.dateValue = t;
19500             if(t == today){
19501                 cell.className += " fc-today";
19502                 cell.className += " fc-state-highlight";
19503                 cell.title = cal.todayText;
19504             }
19505             if(t == sel){
19506                 // disable highlight in other month..
19507                 //cell.className += " fc-state-highlight";
19508                 
19509             }
19510             // disabling
19511             if(t < min) {
19512                 cell.className = " fc-state-disabled";
19513                 cell.title = cal.minText;
19514                 return;
19515             }
19516             if(t > max) {
19517                 cell.className = " fc-state-disabled";
19518                 cell.title = cal.maxText;
19519                 return;
19520             }
19521             if(ddays){
19522                 if(ddays.indexOf(d.getDay()) != -1){
19523                     cell.title = ddaysText;
19524                     cell.className = " fc-state-disabled";
19525                 }
19526             }
19527             if(ddMatch && format){
19528                 var fvalue = d.dateFormat(format);
19529                 if(ddMatch.test(fvalue)){
19530                     cell.title = ddText.replace("%0", fvalue);
19531                     cell.className = " fc-state-disabled";
19532                 }
19533             }
19534             
19535             if (!cell.initialClassName) {
19536                 cell.initialClassName = cell.dom.className;
19537             }
19538             
19539             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19540         };
19541
19542         var i = 0;
19543         
19544         for(; i < startingPos; i++) {
19545             textEls[i].innerHTML = (++prevStart);
19546             d.setDate(d.getDate()+1);
19547             
19548             cells[i].className = "fc-past fc-other-month";
19549             setCellClass(this, cells[i]);
19550         }
19551         
19552         var intDay = 0;
19553         
19554         for(; i < days; i++){
19555             intDay = i - startingPos + 1;
19556             textEls[i].innerHTML = (intDay);
19557             d.setDate(d.getDate()+1);
19558             
19559             cells[i].className = ''; // "x-date-active";
19560             setCellClass(this, cells[i]);
19561         }
19562         var extraDays = 0;
19563         
19564         for(; i < 42; i++) {
19565             textEls[i].innerHTML = (++extraDays);
19566             d.setDate(d.getDate()+1);
19567             
19568             cells[i].className = "fc-future fc-other-month";
19569             setCellClass(this, cells[i]);
19570         }
19571         
19572         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19573         
19574         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19575         
19576         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19577         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19578         
19579         if(totalRows != 6){
19580             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19581             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19582         }
19583         
19584         this.fireEvent('monthchange', this, date);
19585         
19586         
19587         /*
19588         if(!this.internalRender){
19589             var main = this.el.dom.firstChild;
19590             var w = main.offsetWidth;
19591             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19592             Roo.fly(main).setWidth(w);
19593             this.internalRender = true;
19594             // opera does not respect the auto grow header center column
19595             // then, after it gets a width opera refuses to recalculate
19596             // without a second pass
19597             if(Roo.isOpera && !this.secondPass){
19598                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19599                 this.secondPass = true;
19600                 this.update.defer(10, this, [date]);
19601             }
19602         }
19603         */
19604         
19605     },
19606     
19607     findCell : function(dt) {
19608         dt = dt.clearTime().getTime();
19609         var ret = false;
19610         this.cells.each(function(c){
19611             //Roo.log("check " +c.dateValue + '?=' + dt);
19612             if(c.dateValue == dt){
19613                 ret = c;
19614                 return false;
19615             }
19616             return true;
19617         });
19618         
19619         return ret;
19620     },
19621     
19622     findCells : function(ev) {
19623         var s = ev.start.clone().clearTime().getTime();
19624        // Roo.log(s);
19625         var e= ev.end.clone().clearTime().getTime();
19626        // Roo.log(e);
19627         var ret = [];
19628         this.cells.each(function(c){
19629              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19630             
19631             if(c.dateValue > e){
19632                 return ;
19633             }
19634             if(c.dateValue < s){
19635                 return ;
19636             }
19637             ret.push(c);
19638         });
19639         
19640         return ret;    
19641     },
19642     
19643 //    findBestRow: function(cells)
19644 //    {
19645 //        var ret = 0;
19646 //        
19647 //        for (var i =0 ; i < cells.length;i++) {
19648 //            ret  = Math.max(cells[i].rows || 0,ret);
19649 //        }
19650 //        return ret;
19651 //        
19652 //    },
19653     
19654     
19655     addItem : function(ev)
19656     {
19657         // look for vertical location slot in
19658         var cells = this.findCells(ev);
19659         
19660 //        ev.row = this.findBestRow(cells);
19661         
19662         // work out the location.
19663         
19664         var crow = false;
19665         var rows = [];
19666         for(var i =0; i < cells.length; i++) {
19667             
19668             cells[i].row = cells[0].row;
19669             
19670             if(i == 0){
19671                 cells[i].row = cells[i].row + 1;
19672             }
19673             
19674             if (!crow) {
19675                 crow = {
19676                     start : cells[i],
19677                     end :  cells[i]
19678                 };
19679                 continue;
19680             }
19681             if (crow.start.getY() == cells[i].getY()) {
19682                 // on same row.
19683                 crow.end = cells[i];
19684                 continue;
19685             }
19686             // different row.
19687             rows.push(crow);
19688             crow = {
19689                 start: cells[i],
19690                 end : cells[i]
19691             };
19692             
19693         }
19694         
19695         rows.push(crow);
19696         ev.els = [];
19697         ev.rows = rows;
19698         ev.cells = cells;
19699         
19700         cells[0].events.push(ev);
19701         
19702         this.calevents.push(ev);
19703     },
19704     
19705     clearEvents: function() {
19706         
19707         if(!this.calevents){
19708             return;
19709         }
19710         
19711         Roo.each(this.cells.elements, function(c){
19712             c.row = 0;
19713             c.events = [];
19714             c.more = [];
19715         });
19716         
19717         Roo.each(this.calevents, function(e) {
19718             Roo.each(e.els, function(el) {
19719                 el.un('mouseenter' ,this.onEventEnter, this);
19720                 el.un('mouseleave' ,this.onEventLeave, this);
19721                 el.remove();
19722             },this);
19723         },this);
19724         
19725         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19726             e.remove();
19727         });
19728         
19729     },
19730     
19731     renderEvents: function()
19732     {   
19733         var _this = this;
19734         
19735         this.cells.each(function(c) {
19736             
19737             if(c.row < 5){
19738                 return;
19739             }
19740             
19741             var ev = c.events;
19742             
19743             var r = 4;
19744             if(c.row != c.events.length){
19745                 r = 4 - (4 - (c.row - c.events.length));
19746             }
19747             
19748             c.events = ev.slice(0, r);
19749             c.more = ev.slice(r);
19750             
19751             if(c.more.length && c.more.length == 1){
19752                 c.events.push(c.more.pop());
19753             }
19754             
19755             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19756             
19757         });
19758             
19759         this.cells.each(function(c) {
19760             
19761             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19762             
19763             
19764             for (var e = 0; e < c.events.length; e++){
19765                 var ev = c.events[e];
19766                 var rows = ev.rows;
19767                 
19768                 for(var i = 0; i < rows.length; i++) {
19769                 
19770                     // how many rows should it span..
19771
19772                     var  cfg = {
19773                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19774                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19775
19776                         unselectable : "on",
19777                         cn : [
19778                             {
19779                                 cls: 'fc-event-inner',
19780                                 cn : [
19781     //                                {
19782     //                                  tag:'span',
19783     //                                  cls: 'fc-event-time',
19784     //                                  html : cells.length > 1 ? '' : ev.time
19785     //                                },
19786                                     {
19787                                       tag:'span',
19788                                       cls: 'fc-event-title',
19789                                       html : String.format('{0}', ev.title)
19790                                     }
19791
19792
19793                                 ]
19794                             },
19795                             {
19796                                 cls: 'ui-resizable-handle ui-resizable-e',
19797                                 html : '&nbsp;&nbsp;&nbsp'
19798                             }
19799
19800                         ]
19801                     };
19802
19803                     if (i == 0) {
19804                         cfg.cls += ' fc-event-start';
19805                     }
19806                     if ((i+1) == rows.length) {
19807                         cfg.cls += ' fc-event-end';
19808                     }
19809
19810                     var ctr = _this.el.select('.fc-event-container',true).first();
19811                     var cg = ctr.createChild(cfg);
19812
19813                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19814                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19815
19816                     var r = (c.more.length) ? 1 : 0;
19817                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19818                     cg.setWidth(ebox.right - sbox.x -2);
19819
19820                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19821                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19822                     cg.on('click', _this.onEventClick, _this, ev);
19823
19824                     ev.els.push(cg);
19825                     
19826                 }
19827                 
19828             }
19829             
19830             
19831             if(c.more.length){
19832                 var  cfg = {
19833                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19834                     style : 'position: absolute',
19835                     unselectable : "on",
19836                     cn : [
19837                         {
19838                             cls: 'fc-event-inner',
19839                             cn : [
19840                                 {
19841                                   tag:'span',
19842                                   cls: 'fc-event-title',
19843                                   html : 'More'
19844                                 }
19845
19846
19847                             ]
19848                         },
19849                         {
19850                             cls: 'ui-resizable-handle ui-resizable-e',
19851                             html : '&nbsp;&nbsp;&nbsp'
19852                         }
19853
19854                     ]
19855                 };
19856
19857                 var ctr = _this.el.select('.fc-event-container',true).first();
19858                 var cg = ctr.createChild(cfg);
19859
19860                 var sbox = c.select('.fc-day-content',true).first().getBox();
19861                 var ebox = c.select('.fc-day-content',true).first().getBox();
19862                 //Roo.log(cg);
19863                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19864                 cg.setWidth(ebox.right - sbox.x -2);
19865
19866                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19867                 
19868             }
19869             
19870         });
19871         
19872         
19873         
19874     },
19875     
19876     onEventEnter: function (e, el,event,d) {
19877         this.fireEvent('evententer', this, el, event);
19878     },
19879     
19880     onEventLeave: function (e, el,event,d) {
19881         this.fireEvent('eventleave', this, el, event);
19882     },
19883     
19884     onEventClick: function (e, el,event,d) {
19885         this.fireEvent('eventclick', this, el, event);
19886     },
19887     
19888     onMonthChange: function () {
19889         this.store.load();
19890     },
19891     
19892     onMoreEventClick: function(e, el, more)
19893     {
19894         var _this = this;
19895         
19896         this.calpopover.placement = 'right';
19897         this.calpopover.setTitle('More');
19898         
19899         this.calpopover.setContent('');
19900         
19901         var ctr = this.calpopover.el.select('.popover-content', true).first();
19902         
19903         Roo.each(more, function(m){
19904             var cfg = {
19905                 cls : 'fc-event-hori fc-event-draggable',
19906                 html : m.title
19907             };
19908             var cg = ctr.createChild(cfg);
19909             
19910             cg.on('click', _this.onEventClick, _this, m);
19911         });
19912         
19913         this.calpopover.show(el);
19914         
19915         
19916     },
19917     
19918     onLoad: function () 
19919     {   
19920         this.calevents = [];
19921         var cal = this;
19922         
19923         if(this.store.getCount() > 0){
19924             this.store.data.each(function(d){
19925                cal.addItem({
19926                     id : d.data.id,
19927                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19928                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19929                     time : d.data.start_time,
19930                     title : d.data.title,
19931                     description : d.data.description,
19932                     venue : d.data.venue
19933                 });
19934             });
19935         }
19936         
19937         this.renderEvents();
19938         
19939         if(this.calevents.length && this.loadMask){
19940             this.maskEl.hide();
19941         }
19942     },
19943     
19944     onBeforeLoad: function()
19945     {
19946         this.clearEvents();
19947         if(this.loadMask){
19948             this.maskEl.show();
19949         }
19950     }
19951 });
19952
19953  
19954  /*
19955  * - LGPL
19956  *
19957  * element
19958  * 
19959  */
19960
19961 /**
19962  * @class Roo.bootstrap.Popover
19963  * @extends Roo.bootstrap.Component
19964  * Bootstrap Popover class
19965  * @cfg {String} html contents of the popover   (or false to use children..)
19966  * @cfg {String} title of popover (or false to hide)
19967  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19968  * @cfg {String} trigger click || hover (or false to trigger manually)
19969  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19970  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19971  *      - if false and it has a 'parent' then it will be automatically added to that element
19972  *      - if string - Roo.get  will be called 
19973  * @cfg {Number} delay - delay before showing
19974  
19975  * @constructor
19976  * Create a new Popover
19977  * @param {Object} config The config object
19978  */
19979
19980 Roo.bootstrap.Popover = function(config){
19981     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19982     
19983     this.addEvents({
19984         // raw events
19985          /**
19986          * @event show
19987          * After the popover show
19988          * 
19989          * @param {Roo.bootstrap.Popover} this
19990          */
19991         "show" : true,
19992         /**
19993          * @event hide
19994          * After the popover hide
19995          * 
19996          * @param {Roo.bootstrap.Popover} this
19997          */
19998         "hide" : true
19999     });
20000 };
20001
20002 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20003     
20004     title: false,
20005     html: false,
20006     
20007     placement : 'right',
20008     trigger : 'hover', // hover
20009     modal : false,
20010     delay : 0,
20011     
20012     over: false,
20013     
20014     can_build_overlaid : false,
20015     
20016     maskEl : false, // the mask element
20017     headerEl : false,
20018     contentEl : false,
20019     alignEl : false, // when show is called with an element - this get's stored.
20020     
20021     getChildContainer : function()
20022     {
20023         return this.contentEl;
20024         
20025     },
20026     getPopoverHeader : function()
20027     {
20028         this.title = true; // flag not to hide it..
20029         this.headerEl.addClass('p-0');
20030         return this.headerEl
20031     },
20032     
20033     
20034     getAutoCreate : function(){
20035          
20036         var cfg = {
20037            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20038            style: 'display:block',
20039            cn : [
20040                 {
20041                     cls : 'arrow'
20042                 },
20043                 {
20044                     cls : 'popover-inner ',
20045                     cn : [
20046                         {
20047                             tag: 'h3',
20048                             cls: 'popover-title popover-header',
20049                             html : this.title === false ? '' : this.title
20050                         },
20051                         {
20052                             cls : 'popover-content popover-body '  + (this.cls || ''),
20053                             html : this.html || ''
20054                         }
20055                     ]
20056                     
20057                 }
20058            ]
20059         };
20060         
20061         return cfg;
20062     },
20063     /**
20064      * @param {string} the title
20065      */
20066     setTitle: function(str)
20067     {
20068         this.title = str;
20069         if (this.el) {
20070             this.headerEl.dom.innerHTML = str;
20071         }
20072         
20073     },
20074     /**
20075      * @param {string} the body content
20076      */
20077     setContent: function(str)
20078     {
20079         this.html = str;
20080         if (this.contentEl) {
20081             this.contentEl.dom.innerHTML = str;
20082         }
20083         
20084     },
20085     // as it get's added to the bottom of the page.
20086     onRender : function(ct, position)
20087     {
20088         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20089         
20090         
20091         
20092         if(!this.el){
20093             var cfg = Roo.apply({},  this.getAutoCreate());
20094             cfg.id = Roo.id();
20095             
20096             if (this.cls) {
20097                 cfg.cls += ' ' + this.cls;
20098             }
20099             if (this.style) {
20100                 cfg.style = this.style;
20101             }
20102             //Roo.log("adding to ");
20103             this.el = Roo.get(document.body).createChild(cfg, position);
20104 //            Roo.log(this.el);
20105         }
20106         
20107         this.contentEl = this.el.select('.popover-content',true).first();
20108         this.headerEl =  this.el.select('.popover-title',true).first();
20109         
20110         var nitems = [];
20111         if(typeof(this.items) != 'undefined'){
20112             var items = this.items;
20113             delete this.items;
20114
20115             for(var i =0;i < items.length;i++) {
20116                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20117             }
20118         }
20119
20120         this.items = nitems;
20121         
20122         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20123         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20124         
20125         
20126         
20127         this.initEvents();
20128     },
20129     
20130     resizeMask : function()
20131     {
20132         this.maskEl.setSize(
20133             Roo.lib.Dom.getViewWidth(true),
20134             Roo.lib.Dom.getViewHeight(true)
20135         );
20136     },
20137     
20138     initEvents : function()
20139     {
20140         
20141         if (!this.modal) { 
20142             Roo.bootstrap.Popover.register(this);
20143         }
20144          
20145         this.arrowEl = this.el.select('.arrow',true).first();
20146         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20147         this.el.enableDisplayMode('block');
20148         this.el.hide();
20149  
20150         
20151         if (this.over === false && !this.parent()) {
20152             return; 
20153         }
20154         if (this.triggers === false) {
20155             return;
20156         }
20157          
20158         // support parent
20159         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20160         var triggers = this.trigger ? this.trigger.split(' ') : [];
20161         Roo.each(triggers, function(trigger) {
20162         
20163             if (trigger == 'click') {
20164                 on_el.on('click', this.toggle, this);
20165             } else if (trigger != 'manual') {
20166                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20167                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20168       
20169                 on_el.on(eventIn  ,this.enter, this);
20170                 on_el.on(eventOut, this.leave, this);
20171             }
20172         }, this);
20173     },
20174     
20175     
20176     // private
20177     timeout : null,
20178     hoverState : null,
20179     
20180     toggle : function () {
20181         this.hoverState == 'in' ? this.leave() : this.enter();
20182     },
20183     
20184     enter : function () {
20185         
20186         clearTimeout(this.timeout);
20187     
20188         this.hoverState = 'in';
20189     
20190         if (!this.delay || !this.delay.show) {
20191             this.show();
20192             return;
20193         }
20194         var _t = this;
20195         this.timeout = setTimeout(function () {
20196             if (_t.hoverState == 'in') {
20197                 _t.show();
20198             }
20199         }, this.delay.show)
20200     },
20201     
20202     leave : function() {
20203         clearTimeout(this.timeout);
20204     
20205         this.hoverState = 'out';
20206     
20207         if (!this.delay || !this.delay.hide) {
20208             this.hide();
20209             return;
20210         }
20211         var _t = this;
20212         this.timeout = setTimeout(function () {
20213             if (_t.hoverState == 'out') {
20214                 _t.hide();
20215             }
20216         }, this.delay.hide)
20217     },
20218     /**
20219      * Show the popover
20220      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20221      * @param {string} (left|right|top|bottom) position
20222      */
20223     show : function (on_el, placement)
20224     {
20225         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20226         on_el = on_el || false; // default to false
20227          
20228         if (!on_el) {
20229             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20230                 on_el = this.parent().el;
20231             } else if (this.over) {
20232                 on_el = Roo.get(this.over);
20233             }
20234             
20235         }
20236         
20237         this.alignEl = Roo.get( on_el );
20238
20239         if (!this.el) {
20240             this.render(document.body);
20241         }
20242         
20243         
20244          
20245         
20246         if (this.title === false) {
20247             this.headerEl.hide();
20248         }
20249         
20250        
20251         this.el.show();
20252         this.el.dom.style.display = 'block';
20253          
20254  
20255         if (this.alignEl) {
20256             this.updatePosition(this.placement, true);
20257              
20258         } else {
20259             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20260             var es = this.el.getSize();
20261             var x = Roo.lib.Dom.getViewWidth()/2;
20262             var y = Roo.lib.Dom.getViewHeight()/2;
20263             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20264             
20265         }
20266
20267         
20268         //var arrow = this.el.select('.arrow',true).first();
20269         //arrow.set(align[2], 
20270         
20271         this.el.addClass('in');
20272         
20273          
20274         
20275         this.hoverState = 'in';
20276         
20277         if (this.modal) {
20278             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20279             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20280             this.maskEl.dom.style.display = 'block';
20281             this.maskEl.addClass('show');
20282         }
20283         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20284  
20285         this.fireEvent('show', this);
20286         
20287     },
20288     /**
20289      * fire this manually after loading a grid in the table for example
20290      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20291      * @param {Boolean} try and move it if we cant get right position.
20292      */
20293     updatePosition : function(placement, try_move)
20294     {
20295         // allow for calling with no parameters
20296         placement = placement   ? placement :  this.placement;
20297         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20298         
20299         this.el.removeClass([
20300             'fade','top','bottom', 'left', 'right','in',
20301             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20302         ]);
20303         this.el.addClass(placement + ' bs-popover-' + placement);
20304         
20305         if (!this.alignEl ) {
20306             return false;
20307         }
20308         
20309         switch (placement) {
20310             case 'right':
20311                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20312                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20313                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20314                     //normal display... or moved up/down.
20315                     this.el.setXY(offset);
20316                     var xy = this.alignEl.getAnchorXY('tr', false);
20317                     xy[0]+=2;xy[1]+=5;
20318                     this.arrowEl.setXY(xy);
20319                     return true;
20320                 }
20321                 // continue through...
20322                 return this.updatePosition('left', false);
20323                 
20324             
20325             case 'left':
20326                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20327                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20328                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20329                     //normal display... or moved up/down.
20330                     this.el.setXY(offset);
20331                     var xy = this.alignEl.getAnchorXY('tl', false);
20332                     xy[0]-=10;xy[1]+=5; // << fix me
20333                     this.arrowEl.setXY(xy);
20334                     return true;
20335                 }
20336                 // call self...
20337                 return this.updatePosition('right', false);
20338             
20339             case 'top':
20340                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20341                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20342                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20343                     //normal display... or moved up/down.
20344                     this.el.setXY(offset);
20345                     var xy = this.alignEl.getAnchorXY('t', false);
20346                     xy[1]-=10; // << fix me
20347                     this.arrowEl.setXY(xy);
20348                     return true;
20349                 }
20350                 // fall through
20351                return this.updatePosition('bottom', false);
20352             
20353             case 'bottom':
20354                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20355                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20356                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20357                     //normal display... or moved up/down.
20358                     this.el.setXY(offset);
20359                     var xy = this.alignEl.getAnchorXY('b', false);
20360                      xy[1]+=2; // << fix me
20361                     this.arrowEl.setXY(xy);
20362                     return true;
20363                 }
20364                 // fall through
20365                 return this.updatePosition('top', false);
20366                 
20367             
20368         }
20369         
20370         
20371         return false;
20372     },
20373     
20374     hide : function()
20375     {
20376         this.el.setXY([0,0]);
20377         this.el.removeClass('in');
20378         this.el.hide();
20379         this.hoverState = null;
20380         this.maskEl.hide(); // always..
20381         this.fireEvent('hide', this);
20382     }
20383     
20384 });
20385
20386
20387 Roo.apply(Roo.bootstrap.Popover, {
20388
20389     alignment : {
20390         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20391         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20392         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20393         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20394     },
20395     
20396     zIndex : 20001,
20397
20398     clickHander : false,
20399     
20400
20401     onMouseDown : function(e)
20402     {
20403         if (!e.getTarget(".roo-popover")) {
20404             this.hideAll();
20405         }
20406          
20407     },
20408     
20409     popups : [],
20410     
20411     register : function(popup)
20412     {
20413         if (!Roo.bootstrap.Popover.clickHandler) {
20414             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20415         }
20416         // hide other popups.
20417         this.hideAll();
20418         this.popups.push(popup);
20419     },
20420     hideAll : function()
20421     {
20422         this.popups.forEach(function(p) {
20423             p.hide();
20424         });
20425     }
20426
20427 });/*
20428  * - LGPL
20429  *
20430  * Card header - holder for the card header elements.
20431  * 
20432  */
20433
20434 /**
20435  * @class Roo.bootstrap.PopoverNav
20436  * @extends Roo.bootstrap.NavGroup
20437  * Bootstrap Popover header navigation class
20438  * @constructor
20439  * Create a new Popover Header Navigation 
20440  * @param {Object} config The config object
20441  */
20442
20443 Roo.bootstrap.PopoverNav = function(config){
20444     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20445 };
20446
20447 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20448     
20449     
20450     container_method : 'getPopoverHeader' 
20451     
20452      
20453     
20454     
20455    
20456 });
20457
20458  
20459
20460  /*
20461  * - LGPL
20462  *
20463  * Progress
20464  * 
20465  */
20466
20467 /**
20468  * @class Roo.bootstrap.Progress
20469  * @extends Roo.bootstrap.Component
20470  * Bootstrap Progress class
20471  * @cfg {Boolean} striped striped of the progress bar
20472  * @cfg {Boolean} active animated of the progress bar
20473  * 
20474  * 
20475  * @constructor
20476  * Create a new Progress
20477  * @param {Object} config The config object
20478  */
20479
20480 Roo.bootstrap.Progress = function(config){
20481     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20482 };
20483
20484 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20485     
20486     striped : false,
20487     active: false,
20488     
20489     getAutoCreate : function(){
20490         var cfg = {
20491             tag: 'div',
20492             cls: 'progress'
20493         };
20494         
20495         
20496         if(this.striped){
20497             cfg.cls += ' progress-striped';
20498         }
20499       
20500         if(this.active){
20501             cfg.cls += ' active';
20502         }
20503         
20504         
20505         return cfg;
20506     }
20507    
20508 });
20509
20510  
20511
20512  /*
20513  * - LGPL
20514  *
20515  * ProgressBar
20516  * 
20517  */
20518
20519 /**
20520  * @class Roo.bootstrap.ProgressBar
20521  * @extends Roo.bootstrap.Component
20522  * Bootstrap ProgressBar class
20523  * @cfg {Number} aria_valuenow aria-value now
20524  * @cfg {Number} aria_valuemin aria-value min
20525  * @cfg {Number} aria_valuemax aria-value max
20526  * @cfg {String} label label for the progress bar
20527  * @cfg {String} panel (success | info | warning | danger )
20528  * @cfg {String} role role of the progress bar
20529  * @cfg {String} sr_only text
20530  * 
20531  * 
20532  * @constructor
20533  * Create a new ProgressBar
20534  * @param {Object} config The config object
20535  */
20536
20537 Roo.bootstrap.ProgressBar = function(config){
20538     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20539 };
20540
20541 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20542     
20543     aria_valuenow : 0,
20544     aria_valuemin : 0,
20545     aria_valuemax : 100,
20546     label : false,
20547     panel : false,
20548     role : false,
20549     sr_only: false,
20550     
20551     getAutoCreate : function()
20552     {
20553         
20554         var cfg = {
20555             tag: 'div',
20556             cls: 'progress-bar',
20557             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20558         };
20559         
20560         if(this.sr_only){
20561             cfg.cn = {
20562                 tag: 'span',
20563                 cls: 'sr-only',
20564                 html: this.sr_only
20565             }
20566         }
20567         
20568         if(this.role){
20569             cfg.role = this.role;
20570         }
20571         
20572         if(this.aria_valuenow){
20573             cfg['aria-valuenow'] = this.aria_valuenow;
20574         }
20575         
20576         if(this.aria_valuemin){
20577             cfg['aria-valuemin'] = this.aria_valuemin;
20578         }
20579         
20580         if(this.aria_valuemax){
20581             cfg['aria-valuemax'] = this.aria_valuemax;
20582         }
20583         
20584         if(this.label && !this.sr_only){
20585             cfg.html = this.label;
20586         }
20587         
20588         if(this.panel){
20589             cfg.cls += ' progress-bar-' + this.panel;
20590         }
20591         
20592         return cfg;
20593     },
20594     
20595     update : function(aria_valuenow)
20596     {
20597         this.aria_valuenow = aria_valuenow;
20598         
20599         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20600     }
20601    
20602 });
20603
20604  
20605
20606  /*
20607  * - LGPL
20608  *
20609  * column
20610  * 
20611  */
20612
20613 /**
20614  * @class Roo.bootstrap.TabGroup
20615  * @extends Roo.bootstrap.Column
20616  * Bootstrap Column class
20617  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20618  * @cfg {Boolean} carousel true to make the group behave like a carousel
20619  * @cfg {Boolean} bullets show bullets for the panels
20620  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20621  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20622  * @cfg {Boolean} showarrow (true|false) show arrow default true
20623  * 
20624  * @constructor
20625  * Create a new TabGroup
20626  * @param {Object} config The config object
20627  */
20628
20629 Roo.bootstrap.TabGroup = function(config){
20630     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20631     if (!this.navId) {
20632         this.navId = Roo.id();
20633     }
20634     this.tabs = [];
20635     Roo.bootstrap.TabGroup.register(this);
20636     
20637 };
20638
20639 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20640     
20641     carousel : false,
20642     transition : false,
20643     bullets : 0,
20644     timer : 0,
20645     autoslide : false,
20646     slideFn : false,
20647     slideOnTouch : false,
20648     showarrow : true,
20649     
20650     getAutoCreate : function()
20651     {
20652         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20653         
20654         cfg.cls += ' tab-content';
20655         
20656         if (this.carousel) {
20657             cfg.cls += ' carousel slide';
20658             
20659             cfg.cn = [{
20660                cls : 'carousel-inner',
20661                cn : []
20662             }];
20663         
20664             if(this.bullets  && !Roo.isTouch){
20665                 
20666                 var bullets = {
20667                     cls : 'carousel-bullets',
20668                     cn : []
20669                 };
20670                
20671                 if(this.bullets_cls){
20672                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20673                 }
20674                 
20675                 bullets.cn.push({
20676                     cls : 'clear'
20677                 });
20678                 
20679                 cfg.cn[0].cn.push(bullets);
20680             }
20681             
20682             if(this.showarrow){
20683                 cfg.cn[0].cn.push({
20684                     tag : 'div',
20685                     class : 'carousel-arrow',
20686                     cn : [
20687                         {
20688                             tag : 'div',
20689                             class : 'carousel-prev',
20690                             cn : [
20691                                 {
20692                                     tag : 'i',
20693                                     class : 'fa fa-chevron-left'
20694                                 }
20695                             ]
20696                         },
20697                         {
20698                             tag : 'div',
20699                             class : 'carousel-next',
20700                             cn : [
20701                                 {
20702                                     tag : 'i',
20703                                     class : 'fa fa-chevron-right'
20704                                 }
20705                             ]
20706                         }
20707                     ]
20708                 });
20709             }
20710             
20711         }
20712         
20713         return cfg;
20714     },
20715     
20716     initEvents:  function()
20717     {
20718 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20719 //            this.el.on("touchstart", this.onTouchStart, this);
20720 //        }
20721         
20722         if(this.autoslide){
20723             var _this = this;
20724             
20725             this.slideFn = window.setInterval(function() {
20726                 _this.showPanelNext();
20727             }, this.timer);
20728         }
20729         
20730         if(this.showarrow){
20731             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20732             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20733         }
20734         
20735         
20736     },
20737     
20738 //    onTouchStart : function(e, el, o)
20739 //    {
20740 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20741 //            return;
20742 //        }
20743 //        
20744 //        this.showPanelNext();
20745 //    },
20746     
20747     
20748     getChildContainer : function()
20749     {
20750         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20751     },
20752     
20753     /**
20754     * register a Navigation item
20755     * @param {Roo.bootstrap.NavItem} the navitem to add
20756     */
20757     register : function(item)
20758     {
20759         this.tabs.push( item);
20760         item.navId = this.navId; // not really needed..
20761         this.addBullet();
20762     
20763     },
20764     
20765     getActivePanel : function()
20766     {
20767         var r = false;
20768         Roo.each(this.tabs, function(t) {
20769             if (t.active) {
20770                 r = t;
20771                 return false;
20772             }
20773             return null;
20774         });
20775         return r;
20776         
20777     },
20778     getPanelByName : function(n)
20779     {
20780         var r = false;
20781         Roo.each(this.tabs, function(t) {
20782             if (t.tabId == n) {
20783                 r = t;
20784                 return false;
20785             }
20786             return null;
20787         });
20788         return r;
20789     },
20790     indexOfPanel : function(p)
20791     {
20792         var r = false;
20793         Roo.each(this.tabs, function(t,i) {
20794             if (t.tabId == p.tabId) {
20795                 r = i;
20796                 return false;
20797             }
20798             return null;
20799         });
20800         return r;
20801     },
20802     /**
20803      * show a specific panel
20804      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20805      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20806      */
20807     showPanel : function (pan)
20808     {
20809         if(this.transition || typeof(pan) == 'undefined'){
20810             Roo.log("waiting for the transitionend");
20811             return false;
20812         }
20813         
20814         if (typeof(pan) == 'number') {
20815             pan = this.tabs[pan];
20816         }
20817         
20818         if (typeof(pan) == 'string') {
20819             pan = this.getPanelByName(pan);
20820         }
20821         
20822         var cur = this.getActivePanel();
20823         
20824         if(!pan || !cur){
20825             Roo.log('pan or acitve pan is undefined');
20826             return false;
20827         }
20828         
20829         if (pan.tabId == this.getActivePanel().tabId) {
20830             return true;
20831         }
20832         
20833         if (false === cur.fireEvent('beforedeactivate')) {
20834             return false;
20835         }
20836         
20837         if(this.bullets > 0 && !Roo.isTouch){
20838             this.setActiveBullet(this.indexOfPanel(pan));
20839         }
20840         
20841         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20842             
20843             //class="carousel-item carousel-item-next carousel-item-left"
20844             
20845             this.transition = true;
20846             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20847             var lr = dir == 'next' ? 'left' : 'right';
20848             pan.el.addClass(dir); // or prev
20849             pan.el.addClass('carousel-item-' + dir); // or prev
20850             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20851             cur.el.addClass(lr); // or right
20852             pan.el.addClass(lr);
20853             cur.el.addClass('carousel-item-' +lr); // or right
20854             pan.el.addClass('carousel-item-' +lr);
20855             
20856             
20857             var _this = this;
20858             cur.el.on('transitionend', function() {
20859                 Roo.log("trans end?");
20860                 
20861                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20862                 pan.setActive(true);
20863                 
20864                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20865                 cur.setActive(false);
20866                 
20867                 _this.transition = false;
20868                 
20869             }, this, { single:  true } );
20870             
20871             return true;
20872         }
20873         
20874         cur.setActive(false);
20875         pan.setActive(true);
20876         
20877         return true;
20878         
20879     },
20880     showPanelNext : function()
20881     {
20882         var i = this.indexOfPanel(this.getActivePanel());
20883         
20884         if (i >= this.tabs.length - 1 && !this.autoslide) {
20885             return;
20886         }
20887         
20888         if (i >= this.tabs.length - 1 && this.autoslide) {
20889             i = -1;
20890         }
20891         
20892         this.showPanel(this.tabs[i+1]);
20893     },
20894     
20895     showPanelPrev : function()
20896     {
20897         var i = this.indexOfPanel(this.getActivePanel());
20898         
20899         if (i  < 1 && !this.autoslide) {
20900             return;
20901         }
20902         
20903         if (i < 1 && this.autoslide) {
20904             i = this.tabs.length;
20905         }
20906         
20907         this.showPanel(this.tabs[i-1]);
20908     },
20909     
20910     
20911     addBullet: function()
20912     {
20913         if(!this.bullets || Roo.isTouch){
20914             return;
20915         }
20916         var ctr = this.el.select('.carousel-bullets',true).first();
20917         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20918         var bullet = ctr.createChild({
20919             cls : 'bullet bullet-' + i
20920         },ctr.dom.lastChild);
20921         
20922         
20923         var _this = this;
20924         
20925         bullet.on('click', (function(e, el, o, ii, t){
20926
20927             e.preventDefault();
20928
20929             this.showPanel(ii);
20930
20931             if(this.autoslide && this.slideFn){
20932                 clearInterval(this.slideFn);
20933                 this.slideFn = window.setInterval(function() {
20934                     _this.showPanelNext();
20935                 }, this.timer);
20936             }
20937
20938         }).createDelegate(this, [i, bullet], true));
20939                 
20940         
20941     },
20942      
20943     setActiveBullet : function(i)
20944     {
20945         if(Roo.isTouch){
20946             return;
20947         }
20948         
20949         Roo.each(this.el.select('.bullet', true).elements, function(el){
20950             el.removeClass('selected');
20951         });
20952
20953         var bullet = this.el.select('.bullet-' + i, true).first();
20954         
20955         if(!bullet){
20956             return;
20957         }
20958         
20959         bullet.addClass('selected');
20960     }
20961     
20962     
20963   
20964 });
20965
20966  
20967
20968  
20969  
20970 Roo.apply(Roo.bootstrap.TabGroup, {
20971     
20972     groups: {},
20973      /**
20974     * register a Navigation Group
20975     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20976     */
20977     register : function(navgrp)
20978     {
20979         this.groups[navgrp.navId] = navgrp;
20980         
20981     },
20982     /**
20983     * fetch a Navigation Group based on the navigation ID
20984     * if one does not exist , it will get created.
20985     * @param {string} the navgroup to add
20986     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20987     */
20988     get: function(navId) {
20989         if (typeof(this.groups[navId]) == 'undefined') {
20990             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20991         }
20992         return this.groups[navId] ;
20993     }
20994     
20995     
20996     
20997 });
20998
20999  /*
21000  * - LGPL
21001  *
21002  * TabPanel
21003  * 
21004  */
21005
21006 /**
21007  * @class Roo.bootstrap.TabPanel
21008  * @extends Roo.bootstrap.Component
21009  * Bootstrap TabPanel class
21010  * @cfg {Boolean} active panel active
21011  * @cfg {String} html panel content
21012  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21013  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21014  * @cfg {String} href click to link..
21015  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21016  * 
21017  * 
21018  * @constructor
21019  * Create a new TabPanel
21020  * @param {Object} config The config object
21021  */
21022
21023 Roo.bootstrap.TabPanel = function(config){
21024     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21025     this.addEvents({
21026         /**
21027              * @event changed
21028              * Fires when the active status changes
21029              * @param {Roo.bootstrap.TabPanel} this
21030              * @param {Boolean} state the new state
21031             
21032          */
21033         'changed': true,
21034         /**
21035              * @event beforedeactivate
21036              * Fires before a tab is de-activated - can be used to do validation on a form.
21037              * @param {Roo.bootstrap.TabPanel} this
21038              * @return {Boolean} false if there is an error
21039             
21040          */
21041         'beforedeactivate': true
21042      });
21043     
21044     this.tabId = this.tabId || Roo.id();
21045   
21046 };
21047
21048 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21049     
21050     active: false,
21051     html: false,
21052     tabId: false,
21053     navId : false,
21054     href : '',
21055     touchSlide : false,
21056     getAutoCreate : function(){
21057         
21058         
21059         var cfg = {
21060             tag: 'div',
21061             // item is needed for carousel - not sure if it has any effect otherwise
21062             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21063             html: this.html || ''
21064         };
21065         
21066         if(this.active){
21067             cfg.cls += ' active';
21068         }
21069         
21070         if(this.tabId){
21071             cfg.tabId = this.tabId;
21072         }
21073         
21074         
21075         
21076         return cfg;
21077     },
21078     
21079     initEvents:  function()
21080     {
21081         var p = this.parent();
21082         
21083         this.navId = this.navId || p.navId;
21084         
21085         if (typeof(this.navId) != 'undefined') {
21086             // not really needed.. but just in case.. parent should be a NavGroup.
21087             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21088             
21089             tg.register(this);
21090             
21091             var i = tg.tabs.length - 1;
21092             
21093             if(this.active && tg.bullets > 0 && i < tg.bullets){
21094                 tg.setActiveBullet(i);
21095             }
21096         }
21097         
21098         this.el.on('click', this.onClick, this);
21099         
21100         if(Roo.isTouch && this.touchSlide){
21101             this.el.on("touchstart", this.onTouchStart, this);
21102             this.el.on("touchmove", this.onTouchMove, this);
21103             this.el.on("touchend", this.onTouchEnd, this);
21104         }
21105         
21106     },
21107     
21108     onRender : function(ct, position)
21109     {
21110         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21111     },
21112     
21113     setActive : function(state)
21114     {
21115         Roo.log("panel - set active " + this.tabId + "=" + state);
21116         
21117         this.active = state;
21118         if (!state) {
21119             this.el.removeClass('active');
21120             
21121         } else  if (!this.el.hasClass('active')) {
21122             this.el.addClass('active');
21123         }
21124         
21125         this.fireEvent('changed', this, state);
21126     },
21127     
21128     onClick : function(e)
21129     {
21130         e.preventDefault();
21131         
21132         if(!this.href.length){
21133             return;
21134         }
21135         
21136         window.location.href = this.href;
21137     },
21138     
21139     startX : 0,
21140     startY : 0,
21141     endX : 0,
21142     endY : 0,
21143     swiping : false,
21144     
21145     onTouchStart : function(e)
21146     {
21147         this.swiping = false;
21148         
21149         this.startX = e.browserEvent.touches[0].clientX;
21150         this.startY = e.browserEvent.touches[0].clientY;
21151     },
21152     
21153     onTouchMove : function(e)
21154     {
21155         this.swiping = true;
21156         
21157         this.endX = e.browserEvent.touches[0].clientX;
21158         this.endY = e.browserEvent.touches[0].clientY;
21159     },
21160     
21161     onTouchEnd : function(e)
21162     {
21163         if(!this.swiping){
21164             this.onClick(e);
21165             return;
21166         }
21167         
21168         var tabGroup = this.parent();
21169         
21170         if(this.endX > this.startX){ // swiping right
21171             tabGroup.showPanelPrev();
21172             return;
21173         }
21174         
21175         if(this.startX > this.endX){ // swiping left
21176             tabGroup.showPanelNext();
21177             return;
21178         }
21179     }
21180     
21181     
21182 });
21183  
21184
21185  
21186
21187  /*
21188  * - LGPL
21189  *
21190  * DateField
21191  * 
21192  */
21193
21194 /**
21195  * @class Roo.bootstrap.DateField
21196  * @extends Roo.bootstrap.Input
21197  * Bootstrap DateField class
21198  * @cfg {Number} weekStart default 0
21199  * @cfg {String} viewMode default empty, (months|years)
21200  * @cfg {String} minViewMode default empty, (months|years)
21201  * @cfg {Number} startDate default -Infinity
21202  * @cfg {Number} endDate default Infinity
21203  * @cfg {Boolean} todayHighlight default false
21204  * @cfg {Boolean} todayBtn default false
21205  * @cfg {Boolean} calendarWeeks default false
21206  * @cfg {Object} daysOfWeekDisabled default empty
21207  * @cfg {Boolean} singleMode default false (true | false)
21208  * 
21209  * @cfg {Boolean} keyboardNavigation default true
21210  * @cfg {String} language default en
21211  * 
21212  * @constructor
21213  * Create a new DateField
21214  * @param {Object} config The config object
21215  */
21216
21217 Roo.bootstrap.DateField = function(config){
21218     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21219      this.addEvents({
21220             /**
21221              * @event show
21222              * Fires when this field show.
21223              * @param {Roo.bootstrap.DateField} this
21224              * @param {Mixed} date The date value
21225              */
21226             show : true,
21227             /**
21228              * @event show
21229              * Fires when this field hide.
21230              * @param {Roo.bootstrap.DateField} this
21231              * @param {Mixed} date The date value
21232              */
21233             hide : true,
21234             /**
21235              * @event select
21236              * Fires when select a date.
21237              * @param {Roo.bootstrap.DateField} this
21238              * @param {Mixed} date The date value
21239              */
21240             select : true,
21241             /**
21242              * @event beforeselect
21243              * Fires when before select a date.
21244              * @param {Roo.bootstrap.DateField} this
21245              * @param {Mixed} date The date value
21246              */
21247             beforeselect : true
21248         });
21249 };
21250
21251 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21252     
21253     /**
21254      * @cfg {String} format
21255      * The default date format string which can be overriden for localization support.  The format must be
21256      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21257      */
21258     format : "m/d/y",
21259     /**
21260      * @cfg {String} altFormats
21261      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21262      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21263      */
21264     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21265     
21266     weekStart : 0,
21267     
21268     viewMode : '',
21269     
21270     minViewMode : '',
21271     
21272     todayHighlight : false,
21273     
21274     todayBtn: false,
21275     
21276     language: 'en',
21277     
21278     keyboardNavigation: true,
21279     
21280     calendarWeeks: false,
21281     
21282     startDate: -Infinity,
21283     
21284     endDate: Infinity,
21285     
21286     daysOfWeekDisabled: [],
21287     
21288     _events: [],
21289     
21290     singleMode : false,
21291     
21292     UTCDate: function()
21293     {
21294         return new Date(Date.UTC.apply(Date, arguments));
21295     },
21296     
21297     UTCToday: function()
21298     {
21299         var today = new Date();
21300         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21301     },
21302     
21303     getDate: function() {
21304             var d = this.getUTCDate();
21305             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21306     },
21307     
21308     getUTCDate: function() {
21309             return this.date;
21310     },
21311     
21312     setDate: function(d) {
21313             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21314     },
21315     
21316     setUTCDate: function(d) {
21317             this.date = d;
21318             this.setValue(this.formatDate(this.date));
21319     },
21320         
21321     onRender: function(ct, position)
21322     {
21323         
21324         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21325         
21326         this.language = this.language || 'en';
21327         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21328         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21329         
21330         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21331         this.format = this.format || 'm/d/y';
21332         this.isInline = false;
21333         this.isInput = true;
21334         this.component = this.el.select('.add-on', true).first() || false;
21335         this.component = (this.component && this.component.length === 0) ? false : this.component;
21336         this.hasInput = this.component && this.inputEl().length;
21337         
21338         if (typeof(this.minViewMode === 'string')) {
21339             switch (this.minViewMode) {
21340                 case 'months':
21341                     this.minViewMode = 1;
21342                     break;
21343                 case 'years':
21344                     this.minViewMode = 2;
21345                     break;
21346                 default:
21347                     this.minViewMode = 0;
21348                     break;
21349             }
21350         }
21351         
21352         if (typeof(this.viewMode === 'string')) {
21353             switch (this.viewMode) {
21354                 case 'months':
21355                     this.viewMode = 1;
21356                     break;
21357                 case 'years':
21358                     this.viewMode = 2;
21359                     break;
21360                 default:
21361                     this.viewMode = 0;
21362                     break;
21363             }
21364         }
21365                 
21366         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21367         
21368 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21369         
21370         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21371         
21372         this.picker().on('mousedown', this.onMousedown, this);
21373         this.picker().on('click', this.onClick, this);
21374         
21375         this.picker().addClass('datepicker-dropdown');
21376         
21377         this.startViewMode = this.viewMode;
21378         
21379         if(this.singleMode){
21380             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21381                 v.setVisibilityMode(Roo.Element.DISPLAY);
21382                 v.hide();
21383             });
21384             
21385             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21386                 v.setStyle('width', '189px');
21387             });
21388         }
21389         
21390         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21391             if(!this.calendarWeeks){
21392                 v.remove();
21393                 return;
21394             }
21395             
21396             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21397             v.attr('colspan', function(i, val){
21398                 return parseInt(val) + 1;
21399             });
21400         });
21401                         
21402         
21403         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21404         
21405         this.setStartDate(this.startDate);
21406         this.setEndDate(this.endDate);
21407         
21408         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21409         
21410         this.fillDow();
21411         this.fillMonths();
21412         this.update();
21413         this.showMode();
21414         
21415         if(this.isInline) {
21416             this.showPopup();
21417         }
21418     },
21419     
21420     picker : function()
21421     {
21422         return this.pickerEl;
21423 //        return this.el.select('.datepicker', true).first();
21424     },
21425     
21426     fillDow: function()
21427     {
21428         var dowCnt = this.weekStart;
21429         
21430         var dow = {
21431             tag: 'tr',
21432             cn: [
21433                 
21434             ]
21435         };
21436         
21437         if(this.calendarWeeks){
21438             dow.cn.push({
21439                 tag: 'th',
21440                 cls: 'cw',
21441                 html: '&nbsp;'
21442             })
21443         }
21444         
21445         while (dowCnt < this.weekStart + 7) {
21446             dow.cn.push({
21447                 tag: 'th',
21448                 cls: 'dow',
21449                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21450             });
21451         }
21452         
21453         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21454     },
21455     
21456     fillMonths: function()
21457     {    
21458         var i = 0;
21459         var months = this.picker().select('>.datepicker-months td', true).first();
21460         
21461         months.dom.innerHTML = '';
21462         
21463         while (i < 12) {
21464             var month = {
21465                 tag: 'span',
21466                 cls: 'month',
21467                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21468             };
21469             
21470             months.createChild(month);
21471         }
21472         
21473     },
21474     
21475     update: function()
21476     {
21477         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;
21478         
21479         if (this.date < this.startDate) {
21480             this.viewDate = new Date(this.startDate);
21481         } else if (this.date > this.endDate) {
21482             this.viewDate = new Date(this.endDate);
21483         } else {
21484             this.viewDate = new Date(this.date);
21485         }
21486         
21487         this.fill();
21488     },
21489     
21490     fill: function() 
21491     {
21492         var d = new Date(this.viewDate),
21493                 year = d.getUTCFullYear(),
21494                 month = d.getUTCMonth(),
21495                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21496                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21497                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21498                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21499                 currentDate = this.date && this.date.valueOf(),
21500                 today = this.UTCToday();
21501         
21502         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21503         
21504 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21505         
21506 //        this.picker.select('>tfoot th.today').
21507 //                                              .text(dates[this.language].today)
21508 //                                              .toggle(this.todayBtn !== false);
21509     
21510         this.updateNavArrows();
21511         this.fillMonths();
21512                                                 
21513         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21514         
21515         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21516          
21517         prevMonth.setUTCDate(day);
21518         
21519         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21520         
21521         var nextMonth = new Date(prevMonth);
21522         
21523         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21524         
21525         nextMonth = nextMonth.valueOf();
21526         
21527         var fillMonths = false;
21528         
21529         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21530         
21531         while(prevMonth.valueOf() <= nextMonth) {
21532             var clsName = '';
21533             
21534             if (prevMonth.getUTCDay() === this.weekStart) {
21535                 if(fillMonths){
21536                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21537                 }
21538                     
21539                 fillMonths = {
21540                     tag: 'tr',
21541                     cn: []
21542                 };
21543                 
21544                 if(this.calendarWeeks){
21545                     // ISO 8601: First week contains first thursday.
21546                     // ISO also states week starts on Monday, but we can be more abstract here.
21547                     var
21548                     // Start of current week: based on weekstart/current date
21549                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21550                     // Thursday of this week
21551                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21552                     // First Thursday of year, year from thursday
21553                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21554                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21555                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21556                     
21557                     fillMonths.cn.push({
21558                         tag: 'td',
21559                         cls: 'cw',
21560                         html: calWeek
21561                     });
21562                 }
21563             }
21564             
21565             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21566                 clsName += ' old';
21567             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21568                 clsName += ' new';
21569             }
21570             if (this.todayHighlight &&
21571                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21572                 prevMonth.getUTCMonth() == today.getMonth() &&
21573                 prevMonth.getUTCDate() == today.getDate()) {
21574                 clsName += ' today';
21575             }
21576             
21577             if (currentDate && prevMonth.valueOf() === currentDate) {
21578                 clsName += ' active';
21579             }
21580             
21581             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21582                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21583                     clsName += ' disabled';
21584             }
21585             
21586             fillMonths.cn.push({
21587                 tag: 'td',
21588                 cls: 'day ' + clsName,
21589                 html: prevMonth.getDate()
21590             });
21591             
21592             prevMonth.setDate(prevMonth.getDate()+1);
21593         }
21594           
21595         var currentYear = this.date && this.date.getUTCFullYear();
21596         var currentMonth = this.date && this.date.getUTCMonth();
21597         
21598         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21599         
21600         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21601             v.removeClass('active');
21602             
21603             if(currentYear === year && k === currentMonth){
21604                 v.addClass('active');
21605             }
21606             
21607             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21608                 v.addClass('disabled');
21609             }
21610             
21611         });
21612         
21613         
21614         year = parseInt(year/10, 10) * 10;
21615         
21616         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21617         
21618         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21619         
21620         year -= 1;
21621         for (var i = -1; i < 11; i++) {
21622             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21623                 tag: 'span',
21624                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21625                 html: year
21626             });
21627             
21628             year += 1;
21629         }
21630     },
21631     
21632     showMode: function(dir) 
21633     {
21634         if (dir) {
21635             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21636         }
21637         
21638         Roo.each(this.picker().select('>div',true).elements, function(v){
21639             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21640             v.hide();
21641         });
21642         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21643     },
21644     
21645     place: function()
21646     {
21647         if(this.isInline) {
21648             return;
21649         }
21650         
21651         this.picker().removeClass(['bottom', 'top']);
21652         
21653         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21654             /*
21655              * place to the top of element!
21656              *
21657              */
21658             
21659             this.picker().addClass('top');
21660             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21661             
21662             return;
21663         }
21664         
21665         this.picker().addClass('bottom');
21666         
21667         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21668     },
21669     
21670     parseDate : function(value)
21671     {
21672         if(!value || value instanceof Date){
21673             return value;
21674         }
21675         var v = Date.parseDate(value, this.format);
21676         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21677             v = Date.parseDate(value, 'Y-m-d');
21678         }
21679         if(!v && this.altFormats){
21680             if(!this.altFormatsArray){
21681                 this.altFormatsArray = this.altFormats.split("|");
21682             }
21683             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21684                 v = Date.parseDate(value, this.altFormatsArray[i]);
21685             }
21686         }
21687         return v;
21688     },
21689     
21690     formatDate : function(date, fmt)
21691     {   
21692         return (!date || !(date instanceof Date)) ?
21693         date : date.dateFormat(fmt || this.format);
21694     },
21695     
21696     onFocus : function()
21697     {
21698         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21699         this.showPopup();
21700     },
21701     
21702     onBlur : function()
21703     {
21704         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21705         
21706         var d = this.inputEl().getValue();
21707         
21708         this.setValue(d);
21709                 
21710         this.hidePopup();
21711     },
21712     
21713     showPopup : function()
21714     {
21715         this.picker().show();
21716         this.update();
21717         this.place();
21718         
21719         this.fireEvent('showpopup', this, this.date);
21720     },
21721     
21722     hidePopup : function()
21723     {
21724         if(this.isInline) {
21725             return;
21726         }
21727         this.picker().hide();
21728         this.viewMode = this.startViewMode;
21729         this.showMode();
21730         
21731         this.fireEvent('hidepopup', this, this.date);
21732         
21733     },
21734     
21735     onMousedown: function(e)
21736     {
21737         e.stopPropagation();
21738         e.preventDefault();
21739     },
21740     
21741     keyup: function(e)
21742     {
21743         Roo.bootstrap.DateField.superclass.keyup.call(this);
21744         this.update();
21745     },
21746
21747     setValue: function(v)
21748     {
21749         if(this.fireEvent('beforeselect', this, v) !== false){
21750             var d = new Date(this.parseDate(v) ).clearTime();
21751         
21752             if(isNaN(d.getTime())){
21753                 this.date = this.viewDate = '';
21754                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21755                 return;
21756             }
21757
21758             v = this.formatDate(d);
21759
21760             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21761
21762             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21763
21764             this.update();
21765
21766             this.fireEvent('select', this, this.date);
21767         }
21768     },
21769     
21770     getValue: function()
21771     {
21772         return this.formatDate(this.date);
21773     },
21774     
21775     fireKey: function(e)
21776     {
21777         if (!this.picker().isVisible()){
21778             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21779                 this.showPopup();
21780             }
21781             return;
21782         }
21783         
21784         var dateChanged = false,
21785         dir, day, month,
21786         newDate, newViewDate;
21787         
21788         switch(e.keyCode){
21789             case 27: // escape
21790                 this.hidePopup();
21791                 e.preventDefault();
21792                 break;
21793             case 37: // left
21794             case 39: // right
21795                 if (!this.keyboardNavigation) {
21796                     break;
21797                 }
21798                 dir = e.keyCode == 37 ? -1 : 1;
21799                 
21800                 if (e.ctrlKey){
21801                     newDate = this.moveYear(this.date, dir);
21802                     newViewDate = this.moveYear(this.viewDate, dir);
21803                 } else if (e.shiftKey){
21804                     newDate = this.moveMonth(this.date, dir);
21805                     newViewDate = this.moveMonth(this.viewDate, dir);
21806                 } else {
21807                     newDate = new Date(this.date);
21808                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21809                     newViewDate = new Date(this.viewDate);
21810                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21811                 }
21812                 if (this.dateWithinRange(newDate)){
21813                     this.date = newDate;
21814                     this.viewDate = newViewDate;
21815                     this.setValue(this.formatDate(this.date));
21816 //                    this.update();
21817                     e.preventDefault();
21818                     dateChanged = true;
21819                 }
21820                 break;
21821             case 38: // up
21822             case 40: // down
21823                 if (!this.keyboardNavigation) {
21824                     break;
21825                 }
21826                 dir = e.keyCode == 38 ? -1 : 1;
21827                 if (e.ctrlKey){
21828                     newDate = this.moveYear(this.date, dir);
21829                     newViewDate = this.moveYear(this.viewDate, dir);
21830                 } else if (e.shiftKey){
21831                     newDate = this.moveMonth(this.date, dir);
21832                     newViewDate = this.moveMonth(this.viewDate, dir);
21833                 } else {
21834                     newDate = new Date(this.date);
21835                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21836                     newViewDate = new Date(this.viewDate);
21837                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21838                 }
21839                 if (this.dateWithinRange(newDate)){
21840                     this.date = newDate;
21841                     this.viewDate = newViewDate;
21842                     this.setValue(this.formatDate(this.date));
21843 //                    this.update();
21844                     e.preventDefault();
21845                     dateChanged = true;
21846                 }
21847                 break;
21848             case 13: // enter
21849                 this.setValue(this.formatDate(this.date));
21850                 this.hidePopup();
21851                 e.preventDefault();
21852                 break;
21853             case 9: // tab
21854                 this.setValue(this.formatDate(this.date));
21855                 this.hidePopup();
21856                 break;
21857             case 16: // shift
21858             case 17: // ctrl
21859             case 18: // alt
21860                 break;
21861             default :
21862                 this.hidePopup();
21863                 
21864         }
21865     },
21866     
21867     
21868     onClick: function(e) 
21869     {
21870         e.stopPropagation();
21871         e.preventDefault();
21872         
21873         var target = e.getTarget();
21874         
21875         if(target.nodeName.toLowerCase() === 'i'){
21876             target = Roo.get(target).dom.parentNode;
21877         }
21878         
21879         var nodeName = target.nodeName;
21880         var className = target.className;
21881         var html = target.innerHTML;
21882         //Roo.log(nodeName);
21883         
21884         switch(nodeName.toLowerCase()) {
21885             case 'th':
21886                 switch(className) {
21887                     case 'switch':
21888                         this.showMode(1);
21889                         break;
21890                     case 'prev':
21891                     case 'next':
21892                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21893                         switch(this.viewMode){
21894                                 case 0:
21895                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21896                                         break;
21897                                 case 1:
21898                                 case 2:
21899                                         this.viewDate = this.moveYear(this.viewDate, dir);
21900                                         break;
21901                         }
21902                         this.fill();
21903                         break;
21904                     case 'today':
21905                         var date = new Date();
21906                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21907 //                        this.fill()
21908                         this.setValue(this.formatDate(this.date));
21909                         
21910                         this.hidePopup();
21911                         break;
21912                 }
21913                 break;
21914             case 'span':
21915                 if (className.indexOf('disabled') < 0) {
21916                     this.viewDate.setUTCDate(1);
21917                     if (className.indexOf('month') > -1) {
21918                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21919                     } else {
21920                         var year = parseInt(html, 10) || 0;
21921                         this.viewDate.setUTCFullYear(year);
21922                         
21923                     }
21924                     
21925                     if(this.singleMode){
21926                         this.setValue(this.formatDate(this.viewDate));
21927                         this.hidePopup();
21928                         return;
21929                     }
21930                     
21931                     this.showMode(-1);
21932                     this.fill();
21933                 }
21934                 break;
21935                 
21936             case 'td':
21937                 //Roo.log(className);
21938                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21939                     var day = parseInt(html, 10) || 1;
21940                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21941                         month = (this.viewDate || new Date()).getUTCMonth();
21942
21943                     if (className.indexOf('old') > -1) {
21944                         if(month === 0 ){
21945                             month = 11;
21946                             year -= 1;
21947                         }else{
21948                             month -= 1;
21949                         }
21950                     } else if (className.indexOf('new') > -1) {
21951                         if (month == 11) {
21952                             month = 0;
21953                             year += 1;
21954                         } else {
21955                             month += 1;
21956                         }
21957                     }
21958                     //Roo.log([year,month,day]);
21959                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21960                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21961 //                    this.fill();
21962                     //Roo.log(this.formatDate(this.date));
21963                     this.setValue(this.formatDate(this.date));
21964                     this.hidePopup();
21965                 }
21966                 break;
21967         }
21968     },
21969     
21970     setStartDate: function(startDate)
21971     {
21972         this.startDate = startDate || -Infinity;
21973         if (this.startDate !== -Infinity) {
21974             this.startDate = this.parseDate(this.startDate);
21975         }
21976         this.update();
21977         this.updateNavArrows();
21978     },
21979
21980     setEndDate: function(endDate)
21981     {
21982         this.endDate = endDate || Infinity;
21983         if (this.endDate !== Infinity) {
21984             this.endDate = this.parseDate(this.endDate);
21985         }
21986         this.update();
21987         this.updateNavArrows();
21988     },
21989     
21990     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21991     {
21992         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21993         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21994             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21995         }
21996         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21997             return parseInt(d, 10);
21998         });
21999         this.update();
22000         this.updateNavArrows();
22001     },
22002     
22003     updateNavArrows: function() 
22004     {
22005         if(this.singleMode){
22006             return;
22007         }
22008         
22009         var d = new Date(this.viewDate),
22010         year = d.getUTCFullYear(),
22011         month = d.getUTCMonth();
22012         
22013         Roo.each(this.picker().select('.prev', true).elements, function(v){
22014             v.show();
22015             switch (this.viewMode) {
22016                 case 0:
22017
22018                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22019                         v.hide();
22020                     }
22021                     break;
22022                 case 1:
22023                 case 2:
22024                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22025                         v.hide();
22026                     }
22027                     break;
22028             }
22029         });
22030         
22031         Roo.each(this.picker().select('.next', true).elements, function(v){
22032             v.show();
22033             switch (this.viewMode) {
22034                 case 0:
22035
22036                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22037                         v.hide();
22038                     }
22039                     break;
22040                 case 1:
22041                 case 2:
22042                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22043                         v.hide();
22044                     }
22045                     break;
22046             }
22047         })
22048     },
22049     
22050     moveMonth: function(date, dir)
22051     {
22052         if (!dir) {
22053             return date;
22054         }
22055         var new_date = new Date(date.valueOf()),
22056         day = new_date.getUTCDate(),
22057         month = new_date.getUTCMonth(),
22058         mag = Math.abs(dir),
22059         new_month, test;
22060         dir = dir > 0 ? 1 : -1;
22061         if (mag == 1){
22062             test = dir == -1
22063             // If going back one month, make sure month is not current month
22064             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22065             ? function(){
22066                 return new_date.getUTCMonth() == month;
22067             }
22068             // If going forward one month, make sure month is as expected
22069             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22070             : function(){
22071                 return new_date.getUTCMonth() != new_month;
22072             };
22073             new_month = month + dir;
22074             new_date.setUTCMonth(new_month);
22075             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22076             if (new_month < 0 || new_month > 11) {
22077                 new_month = (new_month + 12) % 12;
22078             }
22079         } else {
22080             // For magnitudes >1, move one month at a time...
22081             for (var i=0; i<mag; i++) {
22082                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22083                 new_date = this.moveMonth(new_date, dir);
22084             }
22085             // ...then reset the day, keeping it in the new month
22086             new_month = new_date.getUTCMonth();
22087             new_date.setUTCDate(day);
22088             test = function(){
22089                 return new_month != new_date.getUTCMonth();
22090             };
22091         }
22092         // Common date-resetting loop -- if date is beyond end of month, make it
22093         // end of month
22094         while (test()){
22095             new_date.setUTCDate(--day);
22096             new_date.setUTCMonth(new_month);
22097         }
22098         return new_date;
22099     },
22100
22101     moveYear: function(date, dir)
22102     {
22103         return this.moveMonth(date, dir*12);
22104     },
22105
22106     dateWithinRange: function(date)
22107     {
22108         return date >= this.startDate && date <= this.endDate;
22109     },
22110
22111     
22112     remove: function() 
22113     {
22114         this.picker().remove();
22115     },
22116     
22117     validateValue : function(value)
22118     {
22119         if(this.getVisibilityEl().hasClass('hidden')){
22120             return true;
22121         }
22122         
22123         if(value.length < 1)  {
22124             if(this.allowBlank){
22125                 return true;
22126             }
22127             return false;
22128         }
22129         
22130         if(value.length < this.minLength){
22131             return false;
22132         }
22133         if(value.length > this.maxLength){
22134             return false;
22135         }
22136         if(this.vtype){
22137             var vt = Roo.form.VTypes;
22138             if(!vt[this.vtype](value, this)){
22139                 return false;
22140             }
22141         }
22142         if(typeof this.validator == "function"){
22143             var msg = this.validator(value);
22144             if(msg !== true){
22145                 return false;
22146             }
22147         }
22148         
22149         if(this.regex && !this.regex.test(value)){
22150             return false;
22151         }
22152         
22153         if(typeof(this.parseDate(value)) == 'undefined'){
22154             return false;
22155         }
22156         
22157         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22158             return false;
22159         }      
22160         
22161         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22162             return false;
22163         } 
22164         
22165         
22166         return true;
22167     },
22168     
22169     reset : function()
22170     {
22171         this.date = this.viewDate = '';
22172         
22173         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22174     }
22175    
22176 });
22177
22178 Roo.apply(Roo.bootstrap.DateField,  {
22179     
22180     head : {
22181         tag: 'thead',
22182         cn: [
22183         {
22184             tag: 'tr',
22185             cn: [
22186             {
22187                 tag: 'th',
22188                 cls: 'prev',
22189                 html: '<i class="fa fa-arrow-left"/>'
22190             },
22191             {
22192                 tag: 'th',
22193                 cls: 'switch',
22194                 colspan: '5'
22195             },
22196             {
22197                 tag: 'th',
22198                 cls: 'next',
22199                 html: '<i class="fa fa-arrow-right"/>'
22200             }
22201
22202             ]
22203         }
22204         ]
22205     },
22206     
22207     content : {
22208         tag: 'tbody',
22209         cn: [
22210         {
22211             tag: 'tr',
22212             cn: [
22213             {
22214                 tag: 'td',
22215                 colspan: '7'
22216             }
22217             ]
22218         }
22219         ]
22220     },
22221     
22222     footer : {
22223         tag: 'tfoot',
22224         cn: [
22225         {
22226             tag: 'tr',
22227             cn: [
22228             {
22229                 tag: 'th',
22230                 colspan: '7',
22231                 cls: 'today'
22232             }
22233                     
22234             ]
22235         }
22236         ]
22237     },
22238     
22239     dates:{
22240         en: {
22241             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22242             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22243             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22244             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22245             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22246             today: "Today"
22247         }
22248     },
22249     
22250     modes: [
22251     {
22252         clsName: 'days',
22253         navFnc: 'Month',
22254         navStep: 1
22255     },
22256     {
22257         clsName: 'months',
22258         navFnc: 'FullYear',
22259         navStep: 1
22260     },
22261     {
22262         clsName: 'years',
22263         navFnc: 'FullYear',
22264         navStep: 10
22265     }]
22266 });
22267
22268 Roo.apply(Roo.bootstrap.DateField,  {
22269   
22270     template : {
22271         tag: 'div',
22272         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22273         cn: [
22274         {
22275             tag: 'div',
22276             cls: 'datepicker-days',
22277             cn: [
22278             {
22279                 tag: 'table',
22280                 cls: 'table-condensed',
22281                 cn:[
22282                 Roo.bootstrap.DateField.head,
22283                 {
22284                     tag: 'tbody'
22285                 },
22286                 Roo.bootstrap.DateField.footer
22287                 ]
22288             }
22289             ]
22290         },
22291         {
22292             tag: 'div',
22293             cls: 'datepicker-months',
22294             cn: [
22295             {
22296                 tag: 'table',
22297                 cls: 'table-condensed',
22298                 cn:[
22299                 Roo.bootstrap.DateField.head,
22300                 Roo.bootstrap.DateField.content,
22301                 Roo.bootstrap.DateField.footer
22302                 ]
22303             }
22304             ]
22305         },
22306         {
22307             tag: 'div',
22308             cls: 'datepicker-years',
22309             cn: [
22310             {
22311                 tag: 'table',
22312                 cls: 'table-condensed',
22313                 cn:[
22314                 Roo.bootstrap.DateField.head,
22315                 Roo.bootstrap.DateField.content,
22316                 Roo.bootstrap.DateField.footer
22317                 ]
22318             }
22319             ]
22320         }
22321         ]
22322     }
22323 });
22324
22325  
22326
22327  /*
22328  * - LGPL
22329  *
22330  * TimeField
22331  * 
22332  */
22333
22334 /**
22335  * @class Roo.bootstrap.TimeField
22336  * @extends Roo.bootstrap.Input
22337  * Bootstrap DateField class
22338  * 
22339  * 
22340  * @constructor
22341  * Create a new TimeField
22342  * @param {Object} config The config object
22343  */
22344
22345 Roo.bootstrap.TimeField = function(config){
22346     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22347     this.addEvents({
22348             /**
22349              * @event show
22350              * Fires when this field show.
22351              * @param {Roo.bootstrap.DateField} thisthis
22352              * @param {Mixed} date The date value
22353              */
22354             show : true,
22355             /**
22356              * @event show
22357              * Fires when this field hide.
22358              * @param {Roo.bootstrap.DateField} this
22359              * @param {Mixed} date The date value
22360              */
22361             hide : true,
22362             /**
22363              * @event select
22364              * Fires when select a date.
22365              * @param {Roo.bootstrap.DateField} this
22366              * @param {Mixed} date The date value
22367              */
22368             select : true
22369         });
22370 };
22371
22372 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22373     
22374     /**
22375      * @cfg {String} format
22376      * The default time format string which can be overriden for localization support.  The format must be
22377      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22378      */
22379     format : "H:i",
22380
22381     getAutoCreate : function()
22382     {
22383         this.after = '<i class="fa far fa-clock"></i>';
22384         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22385         
22386          
22387     },
22388     onRender: function(ct, position)
22389     {
22390         
22391         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22392                 
22393         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22394         
22395         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22396         
22397         this.pop = this.picker().select('>.datepicker-time',true).first();
22398         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22399         
22400         this.picker().on('mousedown', this.onMousedown, this);
22401         this.picker().on('click', this.onClick, this);
22402         
22403         this.picker().addClass('datepicker-dropdown');
22404     
22405         this.fillTime();
22406         this.update();
22407             
22408         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22409         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22410         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22411         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22412         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22413         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22414
22415     },
22416     
22417     fireKey: function(e){
22418         if (!this.picker().isVisible()){
22419             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22420                 this.show();
22421             }
22422             return;
22423         }
22424
22425         e.preventDefault();
22426         
22427         switch(e.keyCode){
22428             case 27: // escape
22429                 this.hide();
22430                 break;
22431             case 37: // left
22432             case 39: // right
22433                 this.onTogglePeriod();
22434                 break;
22435             case 38: // up
22436                 this.onIncrementMinutes();
22437                 break;
22438             case 40: // down
22439                 this.onDecrementMinutes();
22440                 break;
22441             case 13: // enter
22442             case 9: // tab
22443                 this.setTime();
22444                 break;
22445         }
22446     },
22447     
22448     onClick: function(e) {
22449         e.stopPropagation();
22450         e.preventDefault();
22451     },
22452     
22453     picker : function()
22454     {
22455         return this.pickerEl;
22456     },
22457     
22458     fillTime: function()
22459     {    
22460         var time = this.pop.select('tbody', true).first();
22461         
22462         time.dom.innerHTML = '';
22463         
22464         time.createChild({
22465             tag: 'tr',
22466             cn: [
22467                 {
22468                     tag: 'td',
22469                     cn: [
22470                         {
22471                             tag: 'a',
22472                             href: '#',
22473                             cls: 'btn',
22474                             cn: [
22475                                 {
22476                                     tag: 'i',
22477                                     cls: 'hours-up fa fas fa-chevron-up'
22478                                 }
22479                             ]
22480                         } 
22481                     ]
22482                 },
22483                 {
22484                     tag: 'td',
22485                     cls: 'separator'
22486                 },
22487                 {
22488                     tag: 'td',
22489                     cn: [
22490                         {
22491                             tag: 'a',
22492                             href: '#',
22493                             cls: 'btn',
22494                             cn: [
22495                                 {
22496                                     tag: 'i',
22497                                     cls: 'minutes-up fa fas fa-chevron-up'
22498                                 }
22499                             ]
22500                         }
22501                     ]
22502                 },
22503                 {
22504                     tag: 'td',
22505                     cls: 'separator'
22506                 }
22507             ]
22508         });
22509         
22510         time.createChild({
22511             tag: 'tr',
22512             cn: [
22513                 {
22514                     tag: 'td',
22515                     cn: [
22516                         {
22517                             tag: 'span',
22518                             cls: 'timepicker-hour',
22519                             html: '00'
22520                         }  
22521                     ]
22522                 },
22523                 {
22524                     tag: 'td',
22525                     cls: 'separator',
22526                     html: ':'
22527                 },
22528                 {
22529                     tag: 'td',
22530                     cn: [
22531                         {
22532                             tag: 'span',
22533                             cls: 'timepicker-minute',
22534                             html: '00'
22535                         }  
22536                     ]
22537                 },
22538                 {
22539                     tag: 'td',
22540                     cls: 'separator'
22541                 },
22542                 {
22543                     tag: 'td',
22544                     cn: [
22545                         {
22546                             tag: 'button',
22547                             type: 'button',
22548                             cls: 'btn btn-primary period',
22549                             html: 'AM'
22550                             
22551                         }
22552                     ]
22553                 }
22554             ]
22555         });
22556         
22557         time.createChild({
22558             tag: 'tr',
22559             cn: [
22560                 {
22561                     tag: 'td',
22562                     cn: [
22563                         {
22564                             tag: 'a',
22565                             href: '#',
22566                             cls: 'btn',
22567                             cn: [
22568                                 {
22569                                     tag: 'span',
22570                                     cls: 'hours-down fa fas fa-chevron-down'
22571                                 }
22572                             ]
22573                         }
22574                     ]
22575                 },
22576                 {
22577                     tag: 'td',
22578                     cls: 'separator'
22579                 },
22580                 {
22581                     tag: 'td',
22582                     cn: [
22583                         {
22584                             tag: 'a',
22585                             href: '#',
22586                             cls: 'btn',
22587                             cn: [
22588                                 {
22589                                     tag: 'span',
22590                                     cls: 'minutes-down fa fas fa-chevron-down'
22591                                 }
22592                             ]
22593                         }
22594                     ]
22595                 },
22596                 {
22597                     tag: 'td',
22598                     cls: 'separator'
22599                 }
22600             ]
22601         });
22602         
22603     },
22604     
22605     update: function()
22606     {
22607         
22608         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22609         
22610         this.fill();
22611     },
22612     
22613     fill: function() 
22614     {
22615         var hours = this.time.getHours();
22616         var minutes = this.time.getMinutes();
22617         var period = 'AM';
22618         
22619         if(hours > 11){
22620             period = 'PM';
22621         }
22622         
22623         if(hours == 0){
22624             hours = 12;
22625         }
22626         
22627         
22628         if(hours > 12){
22629             hours = hours - 12;
22630         }
22631         
22632         if(hours < 10){
22633             hours = '0' + hours;
22634         }
22635         
22636         if(minutes < 10){
22637             minutes = '0' + minutes;
22638         }
22639         
22640         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22641         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22642         this.pop.select('button', true).first().dom.innerHTML = period;
22643         
22644     },
22645     
22646     place: function()
22647     {   
22648         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22649         
22650         var cls = ['bottom'];
22651         
22652         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22653             cls.pop();
22654             cls.push('top');
22655         }
22656         
22657         cls.push('right');
22658         
22659         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22660             cls.pop();
22661             cls.push('left');
22662         }
22663         //this.picker().setXY(20000,20000);
22664         this.picker().addClass(cls.join('-'));
22665         
22666         var _this = this;
22667         
22668         Roo.each(cls, function(c){
22669             if(c == 'bottom'){
22670                 (function() {
22671                  //  
22672                 }).defer(200);
22673                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22674                 //_this.picker().setTop(_this.inputEl().getHeight());
22675                 return;
22676             }
22677             if(c == 'top'){
22678                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22679                 
22680                 //_this.picker().setTop(0 - _this.picker().getHeight());
22681                 return;
22682             }
22683             /*
22684             if(c == 'left'){
22685                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22686                 return;
22687             }
22688             if(c == 'right'){
22689                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22690                 return;
22691             }
22692             */
22693         });
22694         
22695     },
22696   
22697     onFocus : function()
22698     {
22699         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22700         this.show();
22701     },
22702     
22703     onBlur : function()
22704     {
22705         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22706         this.hide();
22707     },
22708     
22709     show : function()
22710     {
22711         this.picker().show();
22712         this.pop.show();
22713         this.update();
22714         this.place();
22715         
22716         this.fireEvent('show', this, this.date);
22717     },
22718     
22719     hide : function()
22720     {
22721         this.picker().hide();
22722         this.pop.hide();
22723         
22724         this.fireEvent('hide', this, this.date);
22725     },
22726     
22727     setTime : function()
22728     {
22729         this.hide();
22730         this.setValue(this.time.format(this.format));
22731         
22732         this.fireEvent('select', this, this.date);
22733         
22734         
22735     },
22736     
22737     onMousedown: function(e){
22738         e.stopPropagation();
22739         e.preventDefault();
22740     },
22741     
22742     onIncrementHours: function()
22743     {
22744         Roo.log('onIncrementHours');
22745         this.time = this.time.add(Date.HOUR, 1);
22746         this.update();
22747         
22748     },
22749     
22750     onDecrementHours: function()
22751     {
22752         Roo.log('onDecrementHours');
22753         this.time = this.time.add(Date.HOUR, -1);
22754         this.update();
22755     },
22756     
22757     onIncrementMinutes: function()
22758     {
22759         Roo.log('onIncrementMinutes');
22760         this.time = this.time.add(Date.MINUTE, 1);
22761         this.update();
22762     },
22763     
22764     onDecrementMinutes: function()
22765     {
22766         Roo.log('onDecrementMinutes');
22767         this.time = this.time.add(Date.MINUTE, -1);
22768         this.update();
22769     },
22770     
22771     onTogglePeriod: function()
22772     {
22773         Roo.log('onTogglePeriod');
22774         this.time = this.time.add(Date.HOUR, 12);
22775         this.update();
22776     }
22777     
22778    
22779 });
22780  
22781
22782 Roo.apply(Roo.bootstrap.TimeField,  {
22783   
22784     template : {
22785         tag: 'div',
22786         cls: 'datepicker dropdown-menu',
22787         cn: [
22788             {
22789                 tag: 'div',
22790                 cls: 'datepicker-time',
22791                 cn: [
22792                 {
22793                     tag: 'table',
22794                     cls: 'table-condensed',
22795                     cn:[
22796                         {
22797                             tag: 'tbody',
22798                             cn: [
22799                                 {
22800                                     tag: 'tr',
22801                                     cn: [
22802                                     {
22803                                         tag: 'td',
22804                                         colspan: '7'
22805                                     }
22806                                     ]
22807                                 }
22808                             ]
22809                         },
22810                         {
22811                             tag: 'tfoot',
22812                             cn: [
22813                                 {
22814                                     tag: 'tr',
22815                                     cn: [
22816                                     {
22817                                         tag: 'th',
22818                                         colspan: '7',
22819                                         cls: '',
22820                                         cn: [
22821                                             {
22822                                                 tag: 'button',
22823                                                 cls: 'btn btn-info ok',
22824                                                 html: 'OK'
22825                                             }
22826                                         ]
22827                                     }
22828                     
22829                                     ]
22830                                 }
22831                             ]
22832                         }
22833                     ]
22834                 }
22835                 ]
22836             }
22837         ]
22838     }
22839 });
22840
22841  
22842
22843  /*
22844  * - LGPL
22845  *
22846  * MonthField
22847  * 
22848  */
22849
22850 /**
22851  * @class Roo.bootstrap.MonthField
22852  * @extends Roo.bootstrap.Input
22853  * Bootstrap MonthField class
22854  * 
22855  * @cfg {String} language default en
22856  * 
22857  * @constructor
22858  * Create a new MonthField
22859  * @param {Object} config The config object
22860  */
22861
22862 Roo.bootstrap.MonthField = function(config){
22863     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22864     
22865     this.addEvents({
22866         /**
22867          * @event show
22868          * Fires when this field show.
22869          * @param {Roo.bootstrap.MonthField} this
22870          * @param {Mixed} date The date value
22871          */
22872         show : true,
22873         /**
22874          * @event show
22875          * Fires when this field hide.
22876          * @param {Roo.bootstrap.MonthField} this
22877          * @param {Mixed} date The date value
22878          */
22879         hide : true,
22880         /**
22881          * @event select
22882          * Fires when select a date.
22883          * @param {Roo.bootstrap.MonthField} this
22884          * @param {String} oldvalue The old value
22885          * @param {String} newvalue The new value
22886          */
22887         select : true
22888     });
22889 };
22890
22891 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22892     
22893     onRender: function(ct, position)
22894     {
22895         
22896         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22897         
22898         this.language = this.language || 'en';
22899         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22900         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22901         
22902         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22903         this.isInline = false;
22904         this.isInput = true;
22905         this.component = this.el.select('.add-on', true).first() || false;
22906         this.component = (this.component && this.component.length === 0) ? false : this.component;
22907         this.hasInput = this.component && this.inputEL().length;
22908         
22909         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22910         
22911         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22912         
22913         this.picker().on('mousedown', this.onMousedown, this);
22914         this.picker().on('click', this.onClick, this);
22915         
22916         this.picker().addClass('datepicker-dropdown');
22917         
22918         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22919             v.setStyle('width', '189px');
22920         });
22921         
22922         this.fillMonths();
22923         
22924         this.update();
22925         
22926         if(this.isInline) {
22927             this.show();
22928         }
22929         
22930     },
22931     
22932     setValue: function(v, suppressEvent)
22933     {   
22934         var o = this.getValue();
22935         
22936         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22937         
22938         this.update();
22939
22940         if(suppressEvent !== true){
22941             this.fireEvent('select', this, o, v);
22942         }
22943         
22944     },
22945     
22946     getValue: function()
22947     {
22948         return this.value;
22949     },
22950     
22951     onClick: function(e) 
22952     {
22953         e.stopPropagation();
22954         e.preventDefault();
22955         
22956         var target = e.getTarget();
22957         
22958         if(target.nodeName.toLowerCase() === 'i'){
22959             target = Roo.get(target).dom.parentNode;
22960         }
22961         
22962         var nodeName = target.nodeName;
22963         var className = target.className;
22964         var html = target.innerHTML;
22965         
22966         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22967             return;
22968         }
22969         
22970         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22971         
22972         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22973         
22974         this.hide();
22975                         
22976     },
22977     
22978     picker : function()
22979     {
22980         return this.pickerEl;
22981     },
22982     
22983     fillMonths: function()
22984     {    
22985         var i = 0;
22986         var months = this.picker().select('>.datepicker-months td', true).first();
22987         
22988         months.dom.innerHTML = '';
22989         
22990         while (i < 12) {
22991             var month = {
22992                 tag: 'span',
22993                 cls: 'month',
22994                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22995             };
22996             
22997             months.createChild(month);
22998         }
22999         
23000     },
23001     
23002     update: function()
23003     {
23004         var _this = this;
23005         
23006         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23007             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23008         }
23009         
23010         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23011             e.removeClass('active');
23012             
23013             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23014                 e.addClass('active');
23015             }
23016         })
23017     },
23018     
23019     place: function()
23020     {
23021         if(this.isInline) {
23022             return;
23023         }
23024         
23025         this.picker().removeClass(['bottom', 'top']);
23026         
23027         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23028             /*
23029              * place to the top of element!
23030              *
23031              */
23032             
23033             this.picker().addClass('top');
23034             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23035             
23036             return;
23037         }
23038         
23039         this.picker().addClass('bottom');
23040         
23041         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23042     },
23043     
23044     onFocus : function()
23045     {
23046         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23047         this.show();
23048     },
23049     
23050     onBlur : function()
23051     {
23052         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23053         
23054         var d = this.inputEl().getValue();
23055         
23056         this.setValue(d);
23057                 
23058         this.hide();
23059     },
23060     
23061     show : function()
23062     {
23063         this.picker().show();
23064         this.picker().select('>.datepicker-months', true).first().show();
23065         this.update();
23066         this.place();
23067         
23068         this.fireEvent('show', this, this.date);
23069     },
23070     
23071     hide : function()
23072     {
23073         if(this.isInline) {
23074             return;
23075         }
23076         this.picker().hide();
23077         this.fireEvent('hide', this, this.date);
23078         
23079     },
23080     
23081     onMousedown: function(e)
23082     {
23083         e.stopPropagation();
23084         e.preventDefault();
23085     },
23086     
23087     keyup: function(e)
23088     {
23089         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23090         this.update();
23091     },
23092
23093     fireKey: function(e)
23094     {
23095         if (!this.picker().isVisible()){
23096             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23097                 this.show();
23098             }
23099             return;
23100         }
23101         
23102         var dir;
23103         
23104         switch(e.keyCode){
23105             case 27: // escape
23106                 this.hide();
23107                 e.preventDefault();
23108                 break;
23109             case 37: // left
23110             case 39: // right
23111                 dir = e.keyCode == 37 ? -1 : 1;
23112                 
23113                 this.vIndex = this.vIndex + dir;
23114                 
23115                 if(this.vIndex < 0){
23116                     this.vIndex = 0;
23117                 }
23118                 
23119                 if(this.vIndex > 11){
23120                     this.vIndex = 11;
23121                 }
23122                 
23123                 if(isNaN(this.vIndex)){
23124                     this.vIndex = 0;
23125                 }
23126                 
23127                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23128                 
23129                 break;
23130             case 38: // up
23131             case 40: // down
23132                 
23133                 dir = e.keyCode == 38 ? -1 : 1;
23134                 
23135                 this.vIndex = this.vIndex + dir * 4;
23136                 
23137                 if(this.vIndex < 0){
23138                     this.vIndex = 0;
23139                 }
23140                 
23141                 if(this.vIndex > 11){
23142                     this.vIndex = 11;
23143                 }
23144                 
23145                 if(isNaN(this.vIndex)){
23146                     this.vIndex = 0;
23147                 }
23148                 
23149                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23150                 break;
23151                 
23152             case 13: // enter
23153                 
23154                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23155                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23156                 }
23157                 
23158                 this.hide();
23159                 e.preventDefault();
23160                 break;
23161             case 9: // tab
23162                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23163                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23164                 }
23165                 this.hide();
23166                 break;
23167             case 16: // shift
23168             case 17: // ctrl
23169             case 18: // alt
23170                 break;
23171             default :
23172                 this.hide();
23173                 
23174         }
23175     },
23176     
23177     remove: function() 
23178     {
23179         this.picker().remove();
23180     }
23181    
23182 });
23183
23184 Roo.apply(Roo.bootstrap.MonthField,  {
23185     
23186     content : {
23187         tag: 'tbody',
23188         cn: [
23189         {
23190             tag: 'tr',
23191             cn: [
23192             {
23193                 tag: 'td',
23194                 colspan: '7'
23195             }
23196             ]
23197         }
23198         ]
23199     },
23200     
23201     dates:{
23202         en: {
23203             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23204             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23205         }
23206     }
23207 });
23208
23209 Roo.apply(Roo.bootstrap.MonthField,  {
23210   
23211     template : {
23212         tag: 'div',
23213         cls: 'datepicker dropdown-menu roo-dynamic',
23214         cn: [
23215             {
23216                 tag: 'div',
23217                 cls: 'datepicker-months',
23218                 cn: [
23219                 {
23220                     tag: 'table',
23221                     cls: 'table-condensed',
23222                     cn:[
23223                         Roo.bootstrap.DateField.content
23224                     ]
23225                 }
23226                 ]
23227             }
23228         ]
23229     }
23230 });
23231
23232  
23233
23234  
23235  /*
23236  * - LGPL
23237  *
23238  * CheckBox
23239  * 
23240  */
23241
23242 /**
23243  * @class Roo.bootstrap.CheckBox
23244  * @extends Roo.bootstrap.Input
23245  * Bootstrap CheckBox class
23246  * 
23247  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23248  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23249  * @cfg {String} boxLabel The text that appears beside the checkbox
23250  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23251  * @cfg {Boolean} checked initnal the element
23252  * @cfg {Boolean} inline inline the element (default false)
23253  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23254  * @cfg {String} tooltip label tooltip
23255  * 
23256  * @constructor
23257  * Create a new CheckBox
23258  * @param {Object} config The config object
23259  */
23260
23261 Roo.bootstrap.CheckBox = function(config){
23262     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23263    
23264     this.addEvents({
23265         /**
23266         * @event check
23267         * Fires when the element is checked or unchecked.
23268         * @param {Roo.bootstrap.CheckBox} this This input
23269         * @param {Boolean} checked The new checked value
23270         */
23271        check : true,
23272        /**
23273         * @event click
23274         * Fires when the element is click.
23275         * @param {Roo.bootstrap.CheckBox} this This input
23276         */
23277        click : true
23278     });
23279     
23280 };
23281
23282 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23283   
23284     inputType: 'checkbox',
23285     inputValue: 1,
23286     valueOff: 0,
23287     boxLabel: false,
23288     checked: false,
23289     weight : false,
23290     inline: false,
23291     tooltip : '',
23292     
23293     // checkbox success does not make any sense really.. 
23294     invalidClass : "",
23295     validClass : "",
23296     
23297     
23298     getAutoCreate : function()
23299     {
23300         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23301         
23302         var id = Roo.id();
23303         
23304         var cfg = {};
23305         
23306         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23307         
23308         if(this.inline){
23309             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23310         }
23311         
23312         var input =  {
23313             tag: 'input',
23314             id : id,
23315             type : this.inputType,
23316             value : this.inputValue,
23317             cls : 'roo-' + this.inputType, //'form-box',
23318             placeholder : this.placeholder || ''
23319             
23320         };
23321         
23322         if(this.inputType != 'radio'){
23323             var hidden =  {
23324                 tag: 'input',
23325                 type : 'hidden',
23326                 cls : 'roo-hidden-value',
23327                 value : this.checked ? this.inputValue : this.valueOff
23328             };
23329         }
23330         
23331             
23332         if (this.weight) { // Validity check?
23333             cfg.cls += " " + this.inputType + "-" + this.weight;
23334         }
23335         
23336         if (this.disabled) {
23337             input.disabled=true;
23338         }
23339         
23340         if(this.checked){
23341             input.checked = this.checked;
23342         }
23343         
23344         if (this.name) {
23345             
23346             input.name = this.name;
23347             
23348             if(this.inputType != 'radio'){
23349                 hidden.name = this.name;
23350                 input.name = '_hidden_' + this.name;
23351             }
23352         }
23353         
23354         if (this.size) {
23355             input.cls += ' input-' + this.size;
23356         }
23357         
23358         var settings=this;
23359         
23360         ['xs','sm','md','lg'].map(function(size){
23361             if (settings[size]) {
23362                 cfg.cls += ' col-' + size + '-' + settings[size];
23363             }
23364         });
23365         
23366         var inputblock = input;
23367          
23368         if (this.before || this.after) {
23369             
23370             inputblock = {
23371                 cls : 'input-group',
23372                 cn :  [] 
23373             };
23374             
23375             if (this.before) {
23376                 inputblock.cn.push({
23377                     tag :'span',
23378                     cls : 'input-group-addon',
23379                     html : this.before
23380                 });
23381             }
23382             
23383             inputblock.cn.push(input);
23384             
23385             if(this.inputType != 'radio'){
23386                 inputblock.cn.push(hidden);
23387             }
23388             
23389             if (this.after) {
23390                 inputblock.cn.push({
23391                     tag :'span',
23392                     cls : 'input-group-addon',
23393                     html : this.after
23394                 });
23395             }
23396             
23397         }
23398         var boxLabelCfg = false;
23399         
23400         if(this.boxLabel){
23401            
23402             boxLabelCfg = {
23403                 tag: 'label',
23404                 //'for': id, // box label is handled by onclick - so no for...
23405                 cls: 'box-label',
23406                 html: this.boxLabel
23407             };
23408             if(this.tooltip){
23409                 boxLabelCfg.tooltip = this.tooltip;
23410             }
23411              
23412         }
23413         
23414         
23415         if (align ==='left' && this.fieldLabel.length) {
23416 //                Roo.log("left and has label");
23417             cfg.cn = [
23418                 {
23419                     tag: 'label',
23420                     'for' :  id,
23421                     cls : 'control-label',
23422                     html : this.fieldLabel
23423                 },
23424                 {
23425                     cls : "", 
23426                     cn: [
23427                         inputblock
23428                     ]
23429                 }
23430             ];
23431             
23432             if (boxLabelCfg) {
23433                 cfg.cn[1].cn.push(boxLabelCfg);
23434             }
23435             
23436             if(this.labelWidth > 12){
23437                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23438             }
23439             
23440             if(this.labelWidth < 13 && this.labelmd == 0){
23441                 this.labelmd = this.labelWidth;
23442             }
23443             
23444             if(this.labellg > 0){
23445                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23446                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23447             }
23448             
23449             if(this.labelmd > 0){
23450                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23451                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23452             }
23453             
23454             if(this.labelsm > 0){
23455                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23456                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23457             }
23458             
23459             if(this.labelxs > 0){
23460                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23461                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23462             }
23463             
23464         } else if ( this.fieldLabel.length) {
23465 //                Roo.log(" label");
23466                 cfg.cn = [
23467                    
23468                     {
23469                         tag: this.boxLabel ? 'span' : 'label',
23470                         'for': id,
23471                         cls: 'control-label box-input-label',
23472                         //cls : 'input-group-addon',
23473                         html : this.fieldLabel
23474                     },
23475                     
23476                     inputblock
23477                     
23478                 ];
23479                 if (boxLabelCfg) {
23480                     cfg.cn.push(boxLabelCfg);
23481                 }
23482
23483         } else {
23484             
23485 //                Roo.log(" no label && no align");
23486                 cfg.cn = [  inputblock ] ;
23487                 if (boxLabelCfg) {
23488                     cfg.cn.push(boxLabelCfg);
23489                 }
23490
23491                 
23492         }
23493         
23494        
23495         
23496         if(this.inputType != 'radio'){
23497             cfg.cn.push(hidden);
23498         }
23499         
23500         return cfg;
23501         
23502     },
23503     
23504     /**
23505      * return the real input element.
23506      */
23507     inputEl: function ()
23508     {
23509         return this.el.select('input.roo-' + this.inputType,true).first();
23510     },
23511     hiddenEl: function ()
23512     {
23513         return this.el.select('input.roo-hidden-value',true).first();
23514     },
23515     
23516     labelEl: function()
23517     {
23518         return this.el.select('label.control-label',true).first();
23519     },
23520     /* depricated... */
23521     
23522     label: function()
23523     {
23524         return this.labelEl();
23525     },
23526     
23527     boxLabelEl: function()
23528     {
23529         return this.el.select('label.box-label',true).first();
23530     },
23531     
23532     initEvents : function()
23533     {
23534 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23535         
23536         this.inputEl().on('click', this.onClick,  this);
23537         
23538         if (this.boxLabel) { 
23539             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23540         }
23541         
23542         this.startValue = this.getValue();
23543         
23544         if(this.groupId){
23545             Roo.bootstrap.CheckBox.register(this);
23546         }
23547     },
23548     
23549     onClick : function(e)
23550     {   
23551         if(this.fireEvent('click', this, e) !== false){
23552             this.setChecked(!this.checked);
23553         }
23554         
23555     },
23556     
23557     setChecked : function(state,suppressEvent)
23558     {
23559         this.startValue = this.getValue();
23560
23561         if(this.inputType == 'radio'){
23562             
23563             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23564                 e.dom.checked = false;
23565             });
23566             
23567             this.inputEl().dom.checked = true;
23568             
23569             this.inputEl().dom.value = this.inputValue;
23570             
23571             if(suppressEvent !== true){
23572                 this.fireEvent('check', this, true);
23573             }
23574             
23575             this.validate();
23576             
23577             return;
23578         }
23579         
23580         this.checked = state;
23581         
23582         this.inputEl().dom.checked = state;
23583         
23584         
23585         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23586         
23587         if(suppressEvent !== true){
23588             this.fireEvent('check', this, state);
23589         }
23590         
23591         this.validate();
23592     },
23593     
23594     getValue : function()
23595     {
23596         if(this.inputType == 'radio'){
23597             return this.getGroupValue();
23598         }
23599         
23600         return this.hiddenEl().dom.value;
23601         
23602     },
23603     
23604     getGroupValue : function()
23605     {
23606         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23607             return '';
23608         }
23609         
23610         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23611     },
23612     
23613     setValue : function(v,suppressEvent)
23614     {
23615         if(this.inputType == 'radio'){
23616             this.setGroupValue(v, suppressEvent);
23617             return;
23618         }
23619         
23620         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23621         
23622         this.validate();
23623     },
23624     
23625     setGroupValue : function(v, suppressEvent)
23626     {
23627         this.startValue = this.getValue();
23628         
23629         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23630             e.dom.checked = false;
23631             
23632             if(e.dom.value == v){
23633                 e.dom.checked = true;
23634             }
23635         });
23636         
23637         if(suppressEvent !== true){
23638             this.fireEvent('check', this, true);
23639         }
23640
23641         this.validate();
23642         
23643         return;
23644     },
23645     
23646     validate : function()
23647     {
23648         if(this.getVisibilityEl().hasClass('hidden')){
23649             return true;
23650         }
23651         
23652         if(
23653                 this.disabled || 
23654                 (this.inputType == 'radio' && this.validateRadio()) ||
23655                 (this.inputType == 'checkbox' && this.validateCheckbox())
23656         ){
23657             this.markValid();
23658             return true;
23659         }
23660         
23661         this.markInvalid();
23662         return false;
23663     },
23664     
23665     validateRadio : function()
23666     {
23667         if(this.getVisibilityEl().hasClass('hidden')){
23668             return true;
23669         }
23670         
23671         if(this.allowBlank){
23672             return true;
23673         }
23674         
23675         var valid = false;
23676         
23677         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23678             if(!e.dom.checked){
23679                 return;
23680             }
23681             
23682             valid = true;
23683             
23684             return false;
23685         });
23686         
23687         return valid;
23688     },
23689     
23690     validateCheckbox : function()
23691     {
23692         if(!this.groupId){
23693             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23694             //return (this.getValue() == this.inputValue) ? true : false;
23695         }
23696         
23697         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23698         
23699         if(!group){
23700             return false;
23701         }
23702         
23703         var r = false;
23704         
23705         for(var i in group){
23706             if(group[i].el.isVisible(true)){
23707                 r = false;
23708                 break;
23709             }
23710             
23711             r = true;
23712         }
23713         
23714         for(var i in group){
23715             if(r){
23716                 break;
23717             }
23718             
23719             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23720         }
23721         
23722         return r;
23723     },
23724     
23725     /**
23726      * Mark this field as valid
23727      */
23728     markValid : function()
23729     {
23730         var _this = this;
23731         
23732         this.fireEvent('valid', this);
23733         
23734         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23735         
23736         if(this.groupId){
23737             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23738         }
23739         
23740         if(label){
23741             label.markValid();
23742         }
23743
23744         if(this.inputType == 'radio'){
23745             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23746                 var fg = e.findParent('.form-group', false, true);
23747                 if (Roo.bootstrap.version == 3) {
23748                     fg.removeClass([_this.invalidClass, _this.validClass]);
23749                     fg.addClass(_this.validClass);
23750                 } else {
23751                     fg.removeClass(['is-valid', 'is-invalid']);
23752                     fg.addClass('is-valid');
23753                 }
23754             });
23755             
23756             return;
23757         }
23758
23759         if(!this.groupId){
23760             var fg = this.el.findParent('.form-group', false, true);
23761             if (Roo.bootstrap.version == 3) {
23762                 fg.removeClass([this.invalidClass, this.validClass]);
23763                 fg.addClass(this.validClass);
23764             } else {
23765                 fg.removeClass(['is-valid', 'is-invalid']);
23766                 fg.addClass('is-valid');
23767             }
23768             return;
23769         }
23770         
23771         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23772         
23773         if(!group){
23774             return;
23775         }
23776         
23777         for(var i in group){
23778             var fg = group[i].el.findParent('.form-group', false, true);
23779             if (Roo.bootstrap.version == 3) {
23780                 fg.removeClass([this.invalidClass, this.validClass]);
23781                 fg.addClass(this.validClass);
23782             } else {
23783                 fg.removeClass(['is-valid', 'is-invalid']);
23784                 fg.addClass('is-valid');
23785             }
23786         }
23787     },
23788     
23789      /**
23790      * Mark this field as invalid
23791      * @param {String} msg The validation message
23792      */
23793     markInvalid : function(msg)
23794     {
23795         if(this.allowBlank){
23796             return;
23797         }
23798         
23799         var _this = this;
23800         
23801         this.fireEvent('invalid', this, msg);
23802         
23803         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23804         
23805         if(this.groupId){
23806             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23807         }
23808         
23809         if(label){
23810             label.markInvalid();
23811         }
23812             
23813         if(this.inputType == 'radio'){
23814             
23815             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23816                 var fg = e.findParent('.form-group', false, true);
23817                 if (Roo.bootstrap.version == 3) {
23818                     fg.removeClass([_this.invalidClass, _this.validClass]);
23819                     fg.addClass(_this.invalidClass);
23820                 } else {
23821                     fg.removeClass(['is-invalid', 'is-valid']);
23822                     fg.addClass('is-invalid');
23823                 }
23824             });
23825             
23826             return;
23827         }
23828         
23829         if(!this.groupId){
23830             var fg = this.el.findParent('.form-group', false, true);
23831             if (Roo.bootstrap.version == 3) {
23832                 fg.removeClass([_this.invalidClass, _this.validClass]);
23833                 fg.addClass(_this.invalidClass);
23834             } else {
23835                 fg.removeClass(['is-invalid', 'is-valid']);
23836                 fg.addClass('is-invalid');
23837             }
23838             return;
23839         }
23840         
23841         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23842         
23843         if(!group){
23844             return;
23845         }
23846         
23847         for(var i in group){
23848             var fg = group[i].el.findParent('.form-group', false, true);
23849             if (Roo.bootstrap.version == 3) {
23850                 fg.removeClass([_this.invalidClass, _this.validClass]);
23851                 fg.addClass(_this.invalidClass);
23852             } else {
23853                 fg.removeClass(['is-invalid', 'is-valid']);
23854                 fg.addClass('is-invalid');
23855             }
23856         }
23857         
23858     },
23859     
23860     clearInvalid : function()
23861     {
23862         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23863         
23864         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23865         
23866         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23867         
23868         if (label && label.iconEl) {
23869             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23870             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23871         }
23872     },
23873     
23874     disable : function()
23875     {
23876         if(this.inputType != 'radio'){
23877             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23878             return;
23879         }
23880         
23881         var _this = this;
23882         
23883         if(this.rendered){
23884             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23885                 _this.getActionEl().addClass(this.disabledClass);
23886                 e.dom.disabled = true;
23887             });
23888         }
23889         
23890         this.disabled = true;
23891         this.fireEvent("disable", this);
23892         return this;
23893     },
23894
23895     enable : function()
23896     {
23897         if(this.inputType != 'radio'){
23898             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23899             return;
23900         }
23901         
23902         var _this = this;
23903         
23904         if(this.rendered){
23905             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23906                 _this.getActionEl().removeClass(this.disabledClass);
23907                 e.dom.disabled = false;
23908             });
23909         }
23910         
23911         this.disabled = false;
23912         this.fireEvent("enable", this);
23913         return this;
23914     },
23915     
23916     setBoxLabel : function(v)
23917     {
23918         this.boxLabel = v;
23919         
23920         if(this.rendered){
23921             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23922         }
23923     }
23924
23925 });
23926
23927 Roo.apply(Roo.bootstrap.CheckBox, {
23928     
23929     groups: {},
23930     
23931      /**
23932     * register a CheckBox Group
23933     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23934     */
23935     register : function(checkbox)
23936     {
23937         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23938             this.groups[checkbox.groupId] = {};
23939         }
23940         
23941         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23942             return;
23943         }
23944         
23945         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23946         
23947     },
23948     /**
23949     * fetch a CheckBox Group based on the group ID
23950     * @param {string} the group ID
23951     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23952     */
23953     get: function(groupId) {
23954         if (typeof(this.groups[groupId]) == 'undefined') {
23955             return false;
23956         }
23957         
23958         return this.groups[groupId] ;
23959     }
23960     
23961     
23962 });
23963 /*
23964  * - LGPL
23965  *
23966  * RadioItem
23967  * 
23968  */
23969
23970 /**
23971  * @class Roo.bootstrap.Radio
23972  * @extends Roo.bootstrap.Component
23973  * Bootstrap Radio class
23974  * @cfg {String} boxLabel - the label associated
23975  * @cfg {String} value - the value of radio
23976  * 
23977  * @constructor
23978  * Create a new Radio
23979  * @param {Object} config The config object
23980  */
23981 Roo.bootstrap.Radio = function(config){
23982     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23983     
23984 };
23985
23986 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23987     
23988     boxLabel : '',
23989     
23990     value : '',
23991     
23992     getAutoCreate : function()
23993     {
23994         var cfg = {
23995             tag : 'div',
23996             cls : 'form-group radio',
23997             cn : [
23998                 {
23999                     tag : 'label',
24000                     cls : 'box-label',
24001                     html : this.boxLabel
24002                 }
24003             ]
24004         };
24005         
24006         return cfg;
24007     },
24008     
24009     initEvents : function() 
24010     {
24011         this.parent().register(this);
24012         
24013         this.el.on('click', this.onClick, this);
24014         
24015     },
24016     
24017     onClick : function(e)
24018     {
24019         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24020             this.setChecked(true);
24021         }
24022     },
24023     
24024     setChecked : function(state, suppressEvent)
24025     {
24026         this.parent().setValue(this.value, suppressEvent);
24027         
24028     },
24029     
24030     setBoxLabel : function(v)
24031     {
24032         this.boxLabel = v;
24033         
24034         if(this.rendered){
24035             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24036         }
24037     }
24038     
24039 });
24040  
24041
24042  /*
24043  * - LGPL
24044  *
24045  * Input
24046  * 
24047  */
24048
24049 /**
24050  * @class Roo.bootstrap.SecurePass
24051  * @extends Roo.bootstrap.Input
24052  * Bootstrap SecurePass class
24053  *
24054  * 
24055  * @constructor
24056  * Create a new SecurePass
24057  * @param {Object} config The config object
24058  */
24059  
24060 Roo.bootstrap.SecurePass = function (config) {
24061     // these go here, so the translation tool can replace them..
24062     this.errors = {
24063         PwdEmpty: "Please type a password, and then retype it to confirm.",
24064         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24065         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24066         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24067         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24068         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24069         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24070         TooWeak: "Your password is Too Weak."
24071     },
24072     this.meterLabel = "Password strength:";
24073     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24074     this.meterClass = [
24075         "roo-password-meter-tooweak", 
24076         "roo-password-meter-weak", 
24077         "roo-password-meter-medium", 
24078         "roo-password-meter-strong", 
24079         "roo-password-meter-grey"
24080     ];
24081     
24082     this.errors = {};
24083     
24084     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24085 }
24086
24087 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24088     /**
24089      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24090      * {
24091      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24092      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24093      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24094      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24095      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24096      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24097      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24098      * })
24099      */
24100     // private
24101     
24102     meterWidth: 300,
24103     errorMsg :'',    
24104     errors: false,
24105     imageRoot: '/',
24106     /**
24107      * @cfg {String/Object} Label for the strength meter (defaults to
24108      * 'Password strength:')
24109      */
24110     // private
24111     meterLabel: '',
24112     /**
24113      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24114      * ['Weak', 'Medium', 'Strong'])
24115      */
24116     // private    
24117     pwdStrengths: false,    
24118     // private
24119     strength: 0,
24120     // private
24121     _lastPwd: null,
24122     // private
24123     kCapitalLetter: 0,
24124     kSmallLetter: 1,
24125     kDigit: 2,
24126     kPunctuation: 3,
24127     
24128     insecure: false,
24129     // private
24130     initEvents: function ()
24131     {
24132         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24133
24134         if (this.el.is('input[type=password]') && Roo.isSafari) {
24135             this.el.on('keydown', this.SafariOnKeyDown, this);
24136         }
24137
24138         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24139     },
24140     // private
24141     onRender: function (ct, position)
24142     {
24143         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24144         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24145         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24146
24147         this.trigger.createChild({
24148                    cn: [
24149                     {
24150                     //id: 'PwdMeter',
24151                     tag: 'div',
24152                     cls: 'roo-password-meter-grey col-xs-12',
24153                     style: {
24154                         //width: 0,
24155                         //width: this.meterWidth + 'px'                                                
24156                         }
24157                     },
24158                     {                            
24159                          cls: 'roo-password-meter-text'                          
24160                     }
24161                 ]            
24162         });
24163
24164          
24165         if (this.hideTrigger) {
24166             this.trigger.setDisplayed(false);
24167         }
24168         this.setSize(this.width || '', this.height || '');
24169     },
24170     // private
24171     onDestroy: function ()
24172     {
24173         if (this.trigger) {
24174             this.trigger.removeAllListeners();
24175             this.trigger.remove();
24176         }
24177         if (this.wrap) {
24178             this.wrap.remove();
24179         }
24180         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24181     },
24182     // private
24183     checkStrength: function ()
24184     {
24185         var pwd = this.inputEl().getValue();
24186         if (pwd == this._lastPwd) {
24187             return;
24188         }
24189
24190         var strength;
24191         if (this.ClientSideStrongPassword(pwd)) {
24192             strength = 3;
24193         } else if (this.ClientSideMediumPassword(pwd)) {
24194             strength = 2;
24195         } else if (this.ClientSideWeakPassword(pwd)) {
24196             strength = 1;
24197         } else {
24198             strength = 0;
24199         }
24200         
24201         Roo.log('strength1: ' + strength);
24202         
24203         //var pm = this.trigger.child('div/div/div').dom;
24204         var pm = this.trigger.child('div/div');
24205         pm.removeClass(this.meterClass);
24206         pm.addClass(this.meterClass[strength]);
24207                 
24208         
24209         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24210                 
24211         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24212         
24213         this._lastPwd = pwd;
24214     },
24215     reset: function ()
24216     {
24217         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24218         
24219         this._lastPwd = '';
24220         
24221         var pm = this.trigger.child('div/div');
24222         pm.removeClass(this.meterClass);
24223         pm.addClass('roo-password-meter-grey');        
24224         
24225         
24226         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24227         
24228         pt.innerHTML = '';
24229         this.inputEl().dom.type='password';
24230     },
24231     // private
24232     validateValue: function (value)
24233     {
24234         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24235             return false;
24236         }
24237         if (value.length == 0) {
24238             if (this.allowBlank) {
24239                 this.clearInvalid();
24240                 return true;
24241             }
24242
24243             this.markInvalid(this.errors.PwdEmpty);
24244             this.errorMsg = this.errors.PwdEmpty;
24245             return false;
24246         }
24247         
24248         if(this.insecure){
24249             return true;
24250         }
24251         
24252         if (!value.match(/[\x21-\x7e]+/)) {
24253             this.markInvalid(this.errors.PwdBadChar);
24254             this.errorMsg = this.errors.PwdBadChar;
24255             return false;
24256         }
24257         if (value.length < 6) {
24258             this.markInvalid(this.errors.PwdShort);
24259             this.errorMsg = this.errors.PwdShort;
24260             return false;
24261         }
24262         if (value.length > 16) {
24263             this.markInvalid(this.errors.PwdLong);
24264             this.errorMsg = this.errors.PwdLong;
24265             return false;
24266         }
24267         var strength;
24268         if (this.ClientSideStrongPassword(value)) {
24269             strength = 3;
24270         } else if (this.ClientSideMediumPassword(value)) {
24271             strength = 2;
24272         } else if (this.ClientSideWeakPassword(value)) {
24273             strength = 1;
24274         } else {
24275             strength = 0;
24276         }
24277
24278         
24279         if (strength < 2) {
24280             //this.markInvalid(this.errors.TooWeak);
24281             this.errorMsg = this.errors.TooWeak;
24282             //return false;
24283         }
24284         
24285         
24286         console.log('strength2: ' + strength);
24287         
24288         //var pm = this.trigger.child('div/div/div').dom;
24289         
24290         var pm = this.trigger.child('div/div');
24291         pm.removeClass(this.meterClass);
24292         pm.addClass(this.meterClass[strength]);
24293                 
24294         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24295                 
24296         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24297         
24298         this.errorMsg = ''; 
24299         return true;
24300     },
24301     // private
24302     CharacterSetChecks: function (type)
24303     {
24304         this.type = type;
24305         this.fResult = false;
24306     },
24307     // private
24308     isctype: function (character, type)
24309     {
24310         switch (type) {  
24311             case this.kCapitalLetter:
24312                 if (character >= 'A' && character <= 'Z') {
24313                     return true;
24314                 }
24315                 break;
24316             
24317             case this.kSmallLetter:
24318                 if (character >= 'a' && character <= 'z') {
24319                     return true;
24320                 }
24321                 break;
24322             
24323             case this.kDigit:
24324                 if (character >= '0' && character <= '9') {
24325                     return true;
24326                 }
24327                 break;
24328             
24329             case this.kPunctuation:
24330                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24331                     return true;
24332                 }
24333                 break;
24334             
24335             default:
24336                 return false;
24337         }
24338
24339     },
24340     // private
24341     IsLongEnough: function (pwd, size)
24342     {
24343         return !(pwd == null || isNaN(size) || pwd.length < size);
24344     },
24345     // private
24346     SpansEnoughCharacterSets: function (word, nb)
24347     {
24348         if (!this.IsLongEnough(word, nb))
24349         {
24350             return false;
24351         }
24352
24353         var characterSetChecks = new Array(
24354             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24355             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24356         );
24357         
24358         for (var index = 0; index < word.length; ++index) {
24359             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24360                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24361                     characterSetChecks[nCharSet].fResult = true;
24362                     break;
24363                 }
24364             }
24365         }
24366
24367         var nCharSets = 0;
24368         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24369             if (characterSetChecks[nCharSet].fResult) {
24370                 ++nCharSets;
24371             }
24372         }
24373
24374         if (nCharSets < nb) {
24375             return false;
24376         }
24377         return true;
24378     },
24379     // private
24380     ClientSideStrongPassword: function (pwd)
24381     {
24382         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24383     },
24384     // private
24385     ClientSideMediumPassword: function (pwd)
24386     {
24387         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24388     },
24389     // private
24390     ClientSideWeakPassword: function (pwd)
24391     {
24392         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24393     }
24394           
24395 })//<script type="text/javascript">
24396
24397 /*
24398  * Based  Ext JS Library 1.1.1
24399  * Copyright(c) 2006-2007, Ext JS, LLC.
24400  * LGPL
24401  *
24402  */
24403  
24404 /**
24405  * @class Roo.HtmlEditorCore
24406  * @extends Roo.Component
24407  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24408  *
24409  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24410  */
24411
24412 Roo.HtmlEditorCore = function(config){
24413     
24414     
24415     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24416     
24417     
24418     this.addEvents({
24419         /**
24420          * @event initialize
24421          * Fires when the editor is fully initialized (including the iframe)
24422          * @param {Roo.HtmlEditorCore} this
24423          */
24424         initialize: true,
24425         /**
24426          * @event activate
24427          * Fires when the editor is first receives the focus. Any insertion must wait
24428          * until after this event.
24429          * @param {Roo.HtmlEditorCore} this
24430          */
24431         activate: true,
24432          /**
24433          * @event beforesync
24434          * Fires before the textarea is updated with content from the editor iframe. Return false
24435          * to cancel the sync.
24436          * @param {Roo.HtmlEditorCore} this
24437          * @param {String} html
24438          */
24439         beforesync: true,
24440          /**
24441          * @event beforepush
24442          * Fires before the iframe editor is updated with content from the textarea. Return false
24443          * to cancel the push.
24444          * @param {Roo.HtmlEditorCore} this
24445          * @param {String} html
24446          */
24447         beforepush: true,
24448          /**
24449          * @event sync
24450          * Fires when the textarea is updated with content from the editor iframe.
24451          * @param {Roo.HtmlEditorCore} this
24452          * @param {String} html
24453          */
24454         sync: true,
24455          /**
24456          * @event push
24457          * Fires when the iframe editor is updated with content from the textarea.
24458          * @param {Roo.HtmlEditorCore} this
24459          * @param {String} html
24460          */
24461         push: true,
24462         
24463         /**
24464          * @event editorevent
24465          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24466          * @param {Roo.HtmlEditorCore} this
24467          */
24468         editorevent: true
24469         
24470     });
24471     
24472     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24473     
24474     // defaults : white / black...
24475     this.applyBlacklists();
24476     
24477     
24478     
24479 };
24480
24481
24482 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24483
24484
24485      /**
24486      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24487      */
24488     
24489     owner : false,
24490     
24491      /**
24492      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24493      *                        Roo.resizable.
24494      */
24495     resizable : false,
24496      /**
24497      * @cfg {Number} height (in pixels)
24498      */   
24499     height: 300,
24500    /**
24501      * @cfg {Number} width (in pixels)
24502      */   
24503     width: 500,
24504     
24505     /**
24506      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24507      * 
24508      */
24509     stylesheets: false,
24510     
24511     // id of frame..
24512     frameId: false,
24513     
24514     // private properties
24515     validationEvent : false,
24516     deferHeight: true,
24517     initialized : false,
24518     activated : false,
24519     sourceEditMode : false,
24520     onFocus : Roo.emptyFn,
24521     iframePad:3,
24522     hideMode:'offsets',
24523     
24524     clearUp: true,
24525     
24526     // blacklist + whitelisted elements..
24527     black: false,
24528     white: false,
24529      
24530     bodyCls : '',
24531
24532     /**
24533      * Protected method that will not generally be called directly. It
24534      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24535      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24536      */
24537     getDocMarkup : function(){
24538         // body styles..
24539         var st = '';
24540         
24541         // inherit styels from page...?? 
24542         if (this.stylesheets === false) {
24543             
24544             Roo.get(document.head).select('style').each(function(node) {
24545                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24546             });
24547             
24548             Roo.get(document.head).select('link').each(function(node) { 
24549                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24550             });
24551             
24552         } else if (!this.stylesheets.length) {
24553                 // simple..
24554                 st = '<style type="text/css">' +
24555                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24556                    '</style>';
24557         } else {
24558             for (var i in this.stylesheets) { 
24559                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24560             }
24561             
24562         }
24563         
24564         st +=  '<style type="text/css">' +
24565             'IMG { cursor: pointer } ' +
24566         '</style>';
24567
24568         var cls = 'roo-htmleditor-body';
24569         
24570         if(this.bodyCls.length){
24571             cls += ' ' + this.bodyCls;
24572         }
24573         
24574         return '<html><head>' + st  +
24575             //<style type="text/css">' +
24576             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24577             //'</style>' +
24578             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24579     },
24580
24581     // private
24582     onRender : function(ct, position)
24583     {
24584         var _t = this;
24585         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24586         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24587         
24588         
24589         this.el.dom.style.border = '0 none';
24590         this.el.dom.setAttribute('tabIndex', -1);
24591         this.el.addClass('x-hidden hide');
24592         
24593         
24594         
24595         if(Roo.isIE){ // fix IE 1px bogus margin
24596             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24597         }
24598        
24599         
24600         this.frameId = Roo.id();
24601         
24602          
24603         
24604         var iframe = this.owner.wrap.createChild({
24605             tag: 'iframe',
24606             cls: 'form-control', // bootstrap..
24607             id: this.frameId,
24608             name: this.frameId,
24609             frameBorder : 'no',
24610             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24611         }, this.el
24612         );
24613         
24614         
24615         this.iframe = iframe.dom;
24616
24617          this.assignDocWin();
24618         
24619         this.doc.designMode = 'on';
24620        
24621         this.doc.open();
24622         this.doc.write(this.getDocMarkup());
24623         this.doc.close();
24624
24625         
24626         var task = { // must defer to wait for browser to be ready
24627             run : function(){
24628                 //console.log("run task?" + this.doc.readyState);
24629                 this.assignDocWin();
24630                 if(this.doc.body || this.doc.readyState == 'complete'){
24631                     try {
24632                         this.doc.designMode="on";
24633                     } catch (e) {
24634                         return;
24635                     }
24636                     Roo.TaskMgr.stop(task);
24637                     this.initEditor.defer(10, this);
24638                 }
24639             },
24640             interval : 10,
24641             duration: 10000,
24642             scope: this
24643         };
24644         Roo.TaskMgr.start(task);
24645
24646     },
24647
24648     // private
24649     onResize : function(w, h)
24650     {
24651          Roo.log('resize: ' +w + ',' + h );
24652         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24653         if(!this.iframe){
24654             return;
24655         }
24656         if(typeof w == 'number'){
24657             
24658             this.iframe.style.width = w + 'px';
24659         }
24660         if(typeof h == 'number'){
24661             
24662             this.iframe.style.height = h + 'px';
24663             if(this.doc){
24664                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24665             }
24666         }
24667         
24668     },
24669
24670     /**
24671      * Toggles the editor between standard and source edit mode.
24672      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24673      */
24674     toggleSourceEdit : function(sourceEditMode){
24675         
24676         this.sourceEditMode = sourceEditMode === true;
24677         
24678         if(this.sourceEditMode){
24679  
24680             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24681             
24682         }else{
24683             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24684             //this.iframe.className = '';
24685             this.deferFocus();
24686         }
24687         //this.setSize(this.owner.wrap.getSize());
24688         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24689     },
24690
24691     
24692   
24693
24694     /**
24695      * Protected method that will not generally be called directly. If you need/want
24696      * custom HTML cleanup, this is the method you should override.
24697      * @param {String} html The HTML to be cleaned
24698      * return {String} The cleaned HTML
24699      */
24700     cleanHtml : function(html){
24701         html = String(html);
24702         if(html.length > 5){
24703             if(Roo.isSafari){ // strip safari nonsense
24704                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24705             }
24706         }
24707         if(html == '&nbsp;'){
24708             html = '';
24709         }
24710         return html;
24711     },
24712
24713     /**
24714      * HTML Editor -> Textarea
24715      * Protected method that will not generally be called directly. Syncs the contents
24716      * of the editor iframe with the textarea.
24717      */
24718     syncValue : function(){
24719         if(this.initialized){
24720             var bd = (this.doc.body || this.doc.documentElement);
24721             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24722             var html = bd.innerHTML;
24723             if(Roo.isSafari){
24724                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24725                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24726                 if(m && m[1]){
24727                     html = '<div style="'+m[0]+'">' + html + '</div>';
24728                 }
24729             }
24730             html = this.cleanHtml(html);
24731             // fix up the special chars.. normaly like back quotes in word...
24732             // however we do not want to do this with chinese..
24733             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24734                 
24735                 var cc = match.charCodeAt();
24736
24737                 // Get the character value, handling surrogate pairs
24738                 if (match.length == 2) {
24739                     // It's a surrogate pair, calculate the Unicode code point
24740                     var high = match.charCodeAt(0) - 0xD800;
24741                     var low  = match.charCodeAt(1) - 0xDC00;
24742                     cc = (high * 0x400) + low + 0x10000;
24743                 }  else if (
24744                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24745                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24746                     (cc >= 0xf900 && cc < 0xfb00 )
24747                 ) {
24748                         return match;
24749                 }  
24750          
24751                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24752                 return "&#" + cc + ";";
24753                 
24754                 
24755             });
24756             
24757             
24758              
24759             if(this.owner.fireEvent('beforesync', this, html) !== false){
24760                 this.el.dom.value = html;
24761                 this.owner.fireEvent('sync', this, html);
24762             }
24763         }
24764     },
24765
24766     /**
24767      * Protected method that will not generally be called directly. Pushes the value of the textarea
24768      * into the iframe editor.
24769      */
24770     pushValue : function(){
24771         if(this.initialized){
24772             var v = this.el.dom.value.trim();
24773             
24774 //            if(v.length < 1){
24775 //                v = '&#160;';
24776 //            }
24777             
24778             if(this.owner.fireEvent('beforepush', this, v) !== false){
24779                 var d = (this.doc.body || this.doc.documentElement);
24780                 d.innerHTML = v;
24781                 this.cleanUpPaste();
24782                 this.el.dom.value = d.innerHTML;
24783                 this.owner.fireEvent('push', this, v);
24784             }
24785         }
24786     },
24787
24788     // private
24789     deferFocus : function(){
24790         this.focus.defer(10, this);
24791     },
24792
24793     // doc'ed in Field
24794     focus : function(){
24795         if(this.win && !this.sourceEditMode){
24796             this.win.focus();
24797         }else{
24798             this.el.focus();
24799         }
24800     },
24801     
24802     assignDocWin: function()
24803     {
24804         var iframe = this.iframe;
24805         
24806          if(Roo.isIE){
24807             this.doc = iframe.contentWindow.document;
24808             this.win = iframe.contentWindow;
24809         } else {
24810 //            if (!Roo.get(this.frameId)) {
24811 //                return;
24812 //            }
24813 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24815             
24816             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24817                 return;
24818             }
24819             
24820             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24821             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24822         }
24823     },
24824     
24825     // private
24826     initEditor : function(){
24827         //console.log("INIT EDITOR");
24828         this.assignDocWin();
24829         
24830         
24831         
24832         this.doc.designMode="on";
24833         this.doc.open();
24834         this.doc.write(this.getDocMarkup());
24835         this.doc.close();
24836         
24837         var dbody = (this.doc.body || this.doc.documentElement);
24838         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24839         // this copies styles from the containing element into thsi one..
24840         // not sure why we need all of this..
24841         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24842         
24843         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24844         //ss['background-attachment'] = 'fixed'; // w3c
24845         dbody.bgProperties = 'fixed'; // ie
24846         //Roo.DomHelper.applyStyles(dbody, ss);
24847         Roo.EventManager.on(this.doc, {
24848             //'mousedown': this.onEditorEvent,
24849             'mouseup': this.onEditorEvent,
24850             'dblclick': this.onEditorEvent,
24851             'click': this.onEditorEvent,
24852             'keyup': this.onEditorEvent,
24853             buffer:100,
24854             scope: this
24855         });
24856         if(Roo.isGecko){
24857             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24858         }
24859         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24860             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24861         }
24862         this.initialized = true;
24863
24864         this.owner.fireEvent('initialize', this);
24865         this.pushValue();
24866     },
24867
24868     // private
24869     onDestroy : function(){
24870         
24871         
24872         
24873         if(this.rendered){
24874             
24875             //for (var i =0; i < this.toolbars.length;i++) {
24876             //    // fixme - ask toolbars for heights?
24877             //    this.toolbars[i].onDestroy();
24878            // }
24879             
24880             //this.wrap.dom.innerHTML = '';
24881             //this.wrap.remove();
24882         }
24883     },
24884
24885     // private
24886     onFirstFocus : function(){
24887         
24888         this.assignDocWin();
24889         
24890         
24891         this.activated = true;
24892          
24893     
24894         if(Roo.isGecko){ // prevent silly gecko errors
24895             this.win.focus();
24896             var s = this.win.getSelection();
24897             if(!s.focusNode || s.focusNode.nodeType != 3){
24898                 var r = s.getRangeAt(0);
24899                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24900                 r.collapse(true);
24901                 this.deferFocus();
24902             }
24903             try{
24904                 this.execCmd('useCSS', true);
24905                 this.execCmd('styleWithCSS', false);
24906             }catch(e){}
24907         }
24908         this.owner.fireEvent('activate', this);
24909     },
24910
24911     // private
24912     adjustFont: function(btn){
24913         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24914         //if(Roo.isSafari){ // safari
24915         //    adjust *= 2;
24916        // }
24917         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24918         if(Roo.isSafari){ // safari
24919             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24920             v =  (v < 10) ? 10 : v;
24921             v =  (v > 48) ? 48 : v;
24922             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24923             
24924         }
24925         
24926         
24927         v = Math.max(1, v+adjust);
24928         
24929         this.execCmd('FontSize', v  );
24930     },
24931
24932     onEditorEvent : function(e)
24933     {
24934         this.owner.fireEvent('editorevent', this, e);
24935       //  this.updateToolbar();
24936         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24937     },
24938
24939     insertTag : function(tg)
24940     {
24941         // could be a bit smarter... -> wrap the current selected tRoo..
24942         if (tg.toLowerCase() == 'span' ||
24943             tg.toLowerCase() == 'code' ||
24944             tg.toLowerCase() == 'sup' ||
24945             tg.toLowerCase() == 'sub' 
24946             ) {
24947             
24948             range = this.createRange(this.getSelection());
24949             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24950             wrappingNode.appendChild(range.extractContents());
24951             range.insertNode(wrappingNode);
24952
24953             return;
24954             
24955             
24956             
24957         }
24958         this.execCmd("formatblock",   tg);
24959         
24960     },
24961     
24962     insertText : function(txt)
24963     {
24964         
24965         
24966         var range = this.createRange();
24967         range.deleteContents();
24968                //alert(Sender.getAttribute('label'));
24969                
24970         range.insertNode(this.doc.createTextNode(txt));
24971     } ,
24972     
24973      
24974
24975     /**
24976      * Executes a Midas editor command on the editor document and performs necessary focus and
24977      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24978      * @param {String} cmd The Midas command
24979      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24980      */
24981     relayCmd : function(cmd, value){
24982         this.win.focus();
24983         this.execCmd(cmd, value);
24984         this.owner.fireEvent('editorevent', this);
24985         //this.updateToolbar();
24986         this.owner.deferFocus();
24987     },
24988
24989     /**
24990      * Executes a Midas editor command directly on the editor document.
24991      * For visual commands, you should use {@link #relayCmd} instead.
24992      * <b>This should only be called after the editor is initialized.</b>
24993      * @param {String} cmd The Midas command
24994      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24995      */
24996     execCmd : function(cmd, value){
24997         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24998         this.syncValue();
24999     },
25000  
25001  
25002    
25003     /**
25004      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25005      * to insert tRoo.
25006      * @param {String} text | dom node.. 
25007      */
25008     insertAtCursor : function(text)
25009     {
25010         
25011         if(!this.activated){
25012             return;
25013         }
25014         /*
25015         if(Roo.isIE){
25016             this.win.focus();
25017             var r = this.doc.selection.createRange();
25018             if(r){
25019                 r.collapse(true);
25020                 r.pasteHTML(text);
25021                 this.syncValue();
25022                 this.deferFocus();
25023             
25024             }
25025             return;
25026         }
25027         */
25028         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25029             this.win.focus();
25030             
25031             
25032             // from jquery ui (MIT licenced)
25033             var range, node;
25034             var win = this.win;
25035             
25036             if (win.getSelection && win.getSelection().getRangeAt) {
25037                 range = win.getSelection().getRangeAt(0);
25038                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25039                 range.insertNode(node);
25040             } else if (win.document.selection && win.document.selection.createRange) {
25041                 // no firefox support
25042                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25043                 win.document.selection.createRange().pasteHTML(txt);
25044             } else {
25045                 // no firefox support
25046                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25047                 this.execCmd('InsertHTML', txt);
25048             } 
25049             
25050             this.syncValue();
25051             
25052             this.deferFocus();
25053         }
25054     },
25055  // private
25056     mozKeyPress : function(e){
25057         if(e.ctrlKey){
25058             var c = e.getCharCode(), cmd;
25059           
25060             if(c > 0){
25061                 c = String.fromCharCode(c).toLowerCase();
25062                 switch(c){
25063                     case 'b':
25064                         cmd = 'bold';
25065                         break;
25066                     case 'i':
25067                         cmd = 'italic';
25068                         break;
25069                     
25070                     case 'u':
25071                         cmd = 'underline';
25072                         break;
25073                     
25074                     case 'v':
25075                         this.cleanUpPaste.defer(100, this);
25076                         return;
25077                         
25078                 }
25079                 if(cmd){
25080                     this.win.focus();
25081                     this.execCmd(cmd);
25082                     this.deferFocus();
25083                     e.preventDefault();
25084                 }
25085                 
25086             }
25087         }
25088     },
25089
25090     // private
25091     fixKeys : function(){ // load time branching for fastest keydown performance
25092         if(Roo.isIE){
25093             return function(e){
25094                 var k = e.getKey(), r;
25095                 if(k == e.TAB){
25096                     e.stopEvent();
25097                     r = this.doc.selection.createRange();
25098                     if(r){
25099                         r.collapse(true);
25100                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25101                         this.deferFocus();
25102                     }
25103                     return;
25104                 }
25105                 
25106                 if(k == e.ENTER){
25107                     r = this.doc.selection.createRange();
25108                     if(r){
25109                         var target = r.parentElement();
25110                         if(!target || target.tagName.toLowerCase() != 'li'){
25111                             e.stopEvent();
25112                             r.pasteHTML('<br />');
25113                             r.collapse(false);
25114                             r.select();
25115                         }
25116                     }
25117                 }
25118                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25119                     this.cleanUpPaste.defer(100, this);
25120                     return;
25121                 }
25122                 
25123                 
25124             };
25125         }else if(Roo.isOpera){
25126             return function(e){
25127                 var k = e.getKey();
25128                 if(k == e.TAB){
25129                     e.stopEvent();
25130                     this.win.focus();
25131                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25132                     this.deferFocus();
25133                 }
25134                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25135                     this.cleanUpPaste.defer(100, this);
25136                     return;
25137                 }
25138                 
25139             };
25140         }else if(Roo.isSafari){
25141             return function(e){
25142                 var k = e.getKey();
25143                 
25144                 if(k == e.TAB){
25145                     e.stopEvent();
25146                     this.execCmd('InsertText','\t');
25147                     this.deferFocus();
25148                     return;
25149                 }
25150                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25151                     this.cleanUpPaste.defer(100, this);
25152                     return;
25153                 }
25154                 
25155              };
25156         }
25157     }(),
25158     
25159     getAllAncestors: function()
25160     {
25161         var p = this.getSelectedNode();
25162         var a = [];
25163         if (!p) {
25164             a.push(p); // push blank onto stack..
25165             p = this.getParentElement();
25166         }
25167         
25168         
25169         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25170             a.push(p);
25171             p = p.parentNode;
25172         }
25173         a.push(this.doc.body);
25174         return a;
25175     },
25176     lastSel : false,
25177     lastSelNode : false,
25178     
25179     
25180     getSelection : function() 
25181     {
25182         this.assignDocWin();
25183         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25184     },
25185     
25186     getSelectedNode: function() 
25187     {
25188         // this may only work on Gecko!!!
25189         
25190         // should we cache this!!!!
25191         
25192         
25193         
25194          
25195         var range = this.createRange(this.getSelection()).cloneRange();
25196         
25197         if (Roo.isIE) {
25198             var parent = range.parentElement();
25199             while (true) {
25200                 var testRange = range.duplicate();
25201                 testRange.moveToElementText(parent);
25202                 if (testRange.inRange(range)) {
25203                     break;
25204                 }
25205                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25206                     break;
25207                 }
25208                 parent = parent.parentElement;
25209             }
25210             return parent;
25211         }
25212         
25213         // is ancestor a text element.
25214         var ac =  range.commonAncestorContainer;
25215         if (ac.nodeType == 3) {
25216             ac = ac.parentNode;
25217         }
25218         
25219         var ar = ac.childNodes;
25220          
25221         var nodes = [];
25222         var other_nodes = [];
25223         var has_other_nodes = false;
25224         for (var i=0;i<ar.length;i++) {
25225             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25226                 continue;
25227             }
25228             // fullly contained node.
25229             
25230             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25231                 nodes.push(ar[i]);
25232                 continue;
25233             }
25234             
25235             // probably selected..
25236             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25237                 other_nodes.push(ar[i]);
25238                 continue;
25239             }
25240             // outer..
25241             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25242                 continue;
25243             }
25244             
25245             
25246             has_other_nodes = true;
25247         }
25248         if (!nodes.length && other_nodes.length) {
25249             nodes= other_nodes;
25250         }
25251         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25252             return false;
25253         }
25254         
25255         return nodes[0];
25256     },
25257     createRange: function(sel)
25258     {
25259         // this has strange effects when using with 
25260         // top toolbar - not sure if it's a great idea.
25261         //this.editor.contentWindow.focus();
25262         if (typeof sel != "undefined") {
25263             try {
25264                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25265             } catch(e) {
25266                 return this.doc.createRange();
25267             }
25268         } else {
25269             return this.doc.createRange();
25270         }
25271     },
25272     getParentElement: function()
25273     {
25274         
25275         this.assignDocWin();
25276         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25277         
25278         var range = this.createRange(sel);
25279          
25280         try {
25281             var p = range.commonAncestorContainer;
25282             while (p.nodeType == 3) { // text node
25283                 p = p.parentNode;
25284             }
25285             return p;
25286         } catch (e) {
25287             return null;
25288         }
25289     
25290     },
25291     /***
25292      *
25293      * Range intersection.. the hard stuff...
25294      *  '-1' = before
25295      *  '0' = hits..
25296      *  '1' = after.
25297      *         [ -- selected range --- ]
25298      *   [fail]                        [fail]
25299      *
25300      *    basically..
25301      *      if end is before start or  hits it. fail.
25302      *      if start is after end or hits it fail.
25303      *
25304      *   if either hits (but other is outside. - then it's not 
25305      *   
25306      *    
25307      **/
25308     
25309     
25310     // @see http://www.thismuchiknow.co.uk/?p=64.
25311     rangeIntersectsNode : function(range, node)
25312     {
25313         var nodeRange = node.ownerDocument.createRange();
25314         try {
25315             nodeRange.selectNode(node);
25316         } catch (e) {
25317             nodeRange.selectNodeContents(node);
25318         }
25319     
25320         var rangeStartRange = range.cloneRange();
25321         rangeStartRange.collapse(true);
25322     
25323         var rangeEndRange = range.cloneRange();
25324         rangeEndRange.collapse(false);
25325     
25326         var nodeStartRange = nodeRange.cloneRange();
25327         nodeStartRange.collapse(true);
25328     
25329         var nodeEndRange = nodeRange.cloneRange();
25330         nodeEndRange.collapse(false);
25331     
25332         return rangeStartRange.compareBoundaryPoints(
25333                  Range.START_TO_START, nodeEndRange) == -1 &&
25334                rangeEndRange.compareBoundaryPoints(
25335                  Range.START_TO_START, nodeStartRange) == 1;
25336         
25337          
25338     },
25339     rangeCompareNode : function(range, node)
25340     {
25341         var nodeRange = node.ownerDocument.createRange();
25342         try {
25343             nodeRange.selectNode(node);
25344         } catch (e) {
25345             nodeRange.selectNodeContents(node);
25346         }
25347         
25348         
25349         range.collapse(true);
25350     
25351         nodeRange.collapse(true);
25352      
25353         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25354         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25355          
25356         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25357         
25358         var nodeIsBefore   =  ss == 1;
25359         var nodeIsAfter    = ee == -1;
25360         
25361         if (nodeIsBefore && nodeIsAfter) {
25362             return 0; // outer
25363         }
25364         if (!nodeIsBefore && nodeIsAfter) {
25365             return 1; //right trailed.
25366         }
25367         
25368         if (nodeIsBefore && !nodeIsAfter) {
25369             return 2;  // left trailed.
25370         }
25371         // fully contined.
25372         return 3;
25373     },
25374
25375     // private? - in a new class?
25376     cleanUpPaste :  function()
25377     {
25378         // cleans up the whole document..
25379         Roo.log('cleanuppaste');
25380         
25381         this.cleanUpChildren(this.doc.body);
25382         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25383         if (clean != this.doc.body.innerHTML) {
25384             this.doc.body.innerHTML = clean;
25385         }
25386         
25387     },
25388     
25389     cleanWordChars : function(input) {// change the chars to hex code
25390         var he = Roo.HtmlEditorCore;
25391         
25392         var output = input;
25393         Roo.each(he.swapCodes, function(sw) { 
25394             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25395             
25396             output = output.replace(swapper, sw[1]);
25397         });
25398         
25399         return output;
25400     },
25401     
25402     
25403     cleanUpChildren : function (n)
25404     {
25405         if (!n.childNodes.length) {
25406             return;
25407         }
25408         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25409            this.cleanUpChild(n.childNodes[i]);
25410         }
25411     },
25412     
25413     
25414         
25415     
25416     cleanUpChild : function (node)
25417     {
25418         var ed = this;
25419         //console.log(node);
25420         if (node.nodeName == "#text") {
25421             // clean up silly Windows -- stuff?
25422             return; 
25423         }
25424         if (node.nodeName == "#comment") {
25425             node.parentNode.removeChild(node);
25426             // clean up silly Windows -- stuff?
25427             return; 
25428         }
25429         var lcname = node.tagName.toLowerCase();
25430         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25431         // whitelist of tags..
25432         
25433         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25434             // remove node.
25435             node.parentNode.removeChild(node);
25436             return;
25437             
25438         }
25439         
25440         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25441         
25442         // spans with no attributes - just remove them..
25443         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25444             remove_keep_children = true;
25445         }
25446         
25447         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25448         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25449         
25450         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25451         //    remove_keep_children = true;
25452         //}
25453         
25454         if (remove_keep_children) {
25455             this.cleanUpChildren(node);
25456             // inserts everything just before this node...
25457             while (node.childNodes.length) {
25458                 var cn = node.childNodes[0];
25459                 node.removeChild(cn);
25460                 node.parentNode.insertBefore(cn, node);
25461             }
25462             node.parentNode.removeChild(node);
25463             return;
25464         }
25465         
25466         if (!node.attributes || !node.attributes.length) {
25467             
25468           
25469             
25470             
25471             this.cleanUpChildren(node);
25472             return;
25473         }
25474         
25475         function cleanAttr(n,v)
25476         {
25477             
25478             if (v.match(/^\./) || v.match(/^\//)) {
25479                 return;
25480             }
25481             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25482                 return;
25483             }
25484             if (v.match(/^#/)) {
25485                 return;
25486             }
25487             if (v.match(/^\{/)) { // allow template editing.
25488                 return;
25489             }
25490 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25491             node.removeAttribute(n);
25492             
25493         }
25494         
25495         var cwhite = this.cwhite;
25496         var cblack = this.cblack;
25497             
25498         function cleanStyle(n,v)
25499         {
25500             if (v.match(/expression/)) { //XSS?? should we even bother..
25501                 node.removeAttribute(n);
25502                 return;
25503             }
25504             
25505             var parts = v.split(/;/);
25506             var clean = [];
25507             
25508             Roo.each(parts, function(p) {
25509                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25510                 if (!p.length) {
25511                     return true;
25512                 }
25513                 var l = p.split(':').shift().replace(/\s+/g,'');
25514                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25515                 
25516                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25517 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518                     //node.removeAttribute(n);
25519                     return true;
25520                 }
25521                 //Roo.log()
25522                 // only allow 'c whitelisted system attributes'
25523                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25524 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25525                     //node.removeAttribute(n);
25526                     return true;
25527                 }
25528                 
25529                 
25530                  
25531                 
25532                 clean.push(p);
25533                 return true;
25534             });
25535             if (clean.length) { 
25536                 node.setAttribute(n, clean.join(';'));
25537             } else {
25538                 node.removeAttribute(n);
25539             }
25540             
25541         }
25542         
25543         
25544         for (var i = node.attributes.length-1; i > -1 ; i--) {
25545             var a = node.attributes[i];
25546             //console.log(a);
25547             
25548             if (a.name.toLowerCase().substr(0,2)=='on')  {
25549                 node.removeAttribute(a.name);
25550                 continue;
25551             }
25552             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25553                 node.removeAttribute(a.name);
25554                 continue;
25555             }
25556             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25557                 cleanAttr(a.name,a.value); // fixme..
25558                 continue;
25559             }
25560             if (a.name == 'style') {
25561                 cleanStyle(a.name,a.value);
25562                 continue;
25563             }
25564             /// clean up MS crap..
25565             // tecnically this should be a list of valid class'es..
25566             
25567             
25568             if (a.name == 'class') {
25569                 if (a.value.match(/^Mso/)) {
25570                     node.removeAttribute('class');
25571                 }
25572                 
25573                 if (a.value.match(/^body$/)) {
25574                     node.removeAttribute('class');
25575                 }
25576                 continue;
25577             }
25578             
25579             // style cleanup!?
25580             // class cleanup?
25581             
25582         }
25583         
25584         
25585         this.cleanUpChildren(node);
25586         
25587         
25588     },
25589     
25590     /**
25591      * Clean up MS wordisms...
25592      */
25593     cleanWord : function(node)
25594     {
25595         if (!node) {
25596             this.cleanWord(this.doc.body);
25597             return;
25598         }
25599         
25600         if(
25601                 node.nodeName == 'SPAN' &&
25602                 !node.hasAttributes() &&
25603                 node.childNodes.length == 1 &&
25604                 node.firstChild.nodeName == "#text"  
25605         ) {
25606             var textNode = node.firstChild;
25607             node.removeChild(textNode);
25608             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25609                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25610             }
25611             node.parentNode.insertBefore(textNode, node);
25612             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25613                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25614             }
25615             node.parentNode.removeChild(node);
25616         }
25617         
25618         if (node.nodeName == "#text") {
25619             // clean up silly Windows -- stuff?
25620             return; 
25621         }
25622         if (node.nodeName == "#comment") {
25623             node.parentNode.removeChild(node);
25624             // clean up silly Windows -- stuff?
25625             return; 
25626         }
25627         
25628         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25629             node.parentNode.removeChild(node);
25630             return;
25631         }
25632         //Roo.log(node.tagName);
25633         // remove - but keep children..
25634         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25635             //Roo.log('-- removed');
25636             while (node.childNodes.length) {
25637                 var cn = node.childNodes[0];
25638                 node.removeChild(cn);
25639                 node.parentNode.insertBefore(cn, node);
25640                 // move node to parent - and clean it..
25641                 this.cleanWord(cn);
25642             }
25643             node.parentNode.removeChild(node);
25644             /// no need to iterate chidlren = it's got none..
25645             //this.iterateChildren(node, this.cleanWord);
25646             return;
25647         }
25648         // clean styles
25649         if (node.className.length) {
25650             
25651             var cn = node.className.split(/\W+/);
25652             var cna = [];
25653             Roo.each(cn, function(cls) {
25654                 if (cls.match(/Mso[a-zA-Z]+/)) {
25655                     return;
25656                 }
25657                 cna.push(cls);
25658             });
25659             node.className = cna.length ? cna.join(' ') : '';
25660             if (!cna.length) {
25661                 node.removeAttribute("class");
25662             }
25663         }
25664         
25665         if (node.hasAttribute("lang")) {
25666             node.removeAttribute("lang");
25667         }
25668         
25669         if (node.hasAttribute("style")) {
25670             
25671             var styles = node.getAttribute("style").split(";");
25672             var nstyle = [];
25673             Roo.each(styles, function(s) {
25674                 if (!s.match(/:/)) {
25675                     return;
25676                 }
25677                 var kv = s.split(":");
25678                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25679                     return;
25680                 }
25681                 // what ever is left... we allow.
25682                 nstyle.push(s);
25683             });
25684             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25685             if (!nstyle.length) {
25686                 node.removeAttribute('style');
25687             }
25688         }
25689         this.iterateChildren(node, this.cleanWord);
25690         
25691         
25692         
25693     },
25694     /**
25695      * iterateChildren of a Node, calling fn each time, using this as the scole..
25696      * @param {DomNode} node node to iterate children of.
25697      * @param {Function} fn method of this class to call on each item.
25698      */
25699     iterateChildren : function(node, fn)
25700     {
25701         if (!node.childNodes.length) {
25702                 return;
25703         }
25704         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25705            fn.call(this, node.childNodes[i])
25706         }
25707     },
25708     
25709     
25710     /**
25711      * cleanTableWidths.
25712      *
25713      * Quite often pasting from word etc.. results in tables with column and widths.
25714      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25715      *
25716      */
25717     cleanTableWidths : function(node)
25718     {
25719          
25720          
25721         if (!node) {
25722             this.cleanTableWidths(this.doc.body);
25723             return;
25724         }
25725         
25726         // ignore list...
25727         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25728             return; 
25729         }
25730         Roo.log(node.tagName);
25731         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25732             this.iterateChildren(node, this.cleanTableWidths);
25733             return;
25734         }
25735         if (node.hasAttribute('width')) {
25736             node.removeAttribute('width');
25737         }
25738         
25739          
25740         if (node.hasAttribute("style")) {
25741             // pretty basic...
25742             
25743             var styles = node.getAttribute("style").split(";");
25744             var nstyle = [];
25745             Roo.each(styles, function(s) {
25746                 if (!s.match(/:/)) {
25747                     return;
25748                 }
25749                 var kv = s.split(":");
25750                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25751                     return;
25752                 }
25753                 // what ever is left... we allow.
25754                 nstyle.push(s);
25755             });
25756             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25757             if (!nstyle.length) {
25758                 node.removeAttribute('style');
25759             }
25760         }
25761         
25762         this.iterateChildren(node, this.cleanTableWidths);
25763         
25764         
25765     },
25766     
25767     
25768     
25769     
25770     domToHTML : function(currentElement, depth, nopadtext) {
25771         
25772         depth = depth || 0;
25773         nopadtext = nopadtext || false;
25774     
25775         if (!currentElement) {
25776             return this.domToHTML(this.doc.body);
25777         }
25778         
25779         //Roo.log(currentElement);
25780         var j;
25781         var allText = false;
25782         var nodeName = currentElement.nodeName;
25783         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25784         
25785         if  (nodeName == '#text') {
25786             
25787             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25788         }
25789         
25790         
25791         var ret = '';
25792         if (nodeName != 'BODY') {
25793              
25794             var i = 0;
25795             // Prints the node tagName, such as <A>, <IMG>, etc
25796             if (tagName) {
25797                 var attr = [];
25798                 for(i = 0; i < currentElement.attributes.length;i++) {
25799                     // quoting?
25800                     var aname = currentElement.attributes.item(i).name;
25801                     if (!currentElement.attributes.item(i).value.length) {
25802                         continue;
25803                     }
25804                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25805                 }
25806                 
25807                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25808             } 
25809             else {
25810                 
25811                 // eack
25812             }
25813         } else {
25814             tagName = false;
25815         }
25816         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25817             return ret;
25818         }
25819         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25820             nopadtext = true;
25821         }
25822         
25823         
25824         // Traverse the tree
25825         i = 0;
25826         var currentElementChild = currentElement.childNodes.item(i);
25827         var allText = true;
25828         var innerHTML  = '';
25829         lastnode = '';
25830         while (currentElementChild) {
25831             // Formatting code (indent the tree so it looks nice on the screen)
25832             var nopad = nopadtext;
25833             if (lastnode == 'SPAN') {
25834                 nopad  = true;
25835             }
25836             // text
25837             if  (currentElementChild.nodeName == '#text') {
25838                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25839                 toadd = nopadtext ? toadd : toadd.trim();
25840                 if (!nopad && toadd.length > 80) {
25841                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25842                 }
25843                 innerHTML  += toadd;
25844                 
25845                 i++;
25846                 currentElementChild = currentElement.childNodes.item(i);
25847                 lastNode = '';
25848                 continue;
25849             }
25850             allText = false;
25851             
25852             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25853                 
25854             // Recursively traverse the tree structure of the child node
25855             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25856             lastnode = currentElementChild.nodeName;
25857             i++;
25858             currentElementChild=currentElement.childNodes.item(i);
25859         }
25860         
25861         ret += innerHTML;
25862         
25863         if (!allText) {
25864                 // The remaining code is mostly for formatting the tree
25865             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25866         }
25867         
25868         
25869         if (tagName) {
25870             ret+= "</"+tagName+">";
25871         }
25872         return ret;
25873         
25874     },
25875         
25876     applyBlacklists : function()
25877     {
25878         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25879         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25880         
25881         this.white = [];
25882         this.black = [];
25883         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25884             if (b.indexOf(tag) > -1) {
25885                 return;
25886             }
25887             this.white.push(tag);
25888             
25889         }, this);
25890         
25891         Roo.each(w, function(tag) {
25892             if (b.indexOf(tag) > -1) {
25893                 return;
25894             }
25895             if (this.white.indexOf(tag) > -1) {
25896                 return;
25897             }
25898             this.white.push(tag);
25899             
25900         }, this);
25901         
25902         
25903         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25904             if (w.indexOf(tag) > -1) {
25905                 return;
25906             }
25907             this.black.push(tag);
25908             
25909         }, this);
25910         
25911         Roo.each(b, function(tag) {
25912             if (w.indexOf(tag) > -1) {
25913                 return;
25914             }
25915             if (this.black.indexOf(tag) > -1) {
25916                 return;
25917             }
25918             this.black.push(tag);
25919             
25920         }, this);
25921         
25922         
25923         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25924         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25925         
25926         this.cwhite = [];
25927         this.cblack = [];
25928         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25929             if (b.indexOf(tag) > -1) {
25930                 return;
25931             }
25932             this.cwhite.push(tag);
25933             
25934         }, this);
25935         
25936         Roo.each(w, function(tag) {
25937             if (b.indexOf(tag) > -1) {
25938                 return;
25939             }
25940             if (this.cwhite.indexOf(tag) > -1) {
25941                 return;
25942             }
25943             this.cwhite.push(tag);
25944             
25945         }, this);
25946         
25947         
25948         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25949             if (w.indexOf(tag) > -1) {
25950                 return;
25951             }
25952             this.cblack.push(tag);
25953             
25954         }, this);
25955         
25956         Roo.each(b, function(tag) {
25957             if (w.indexOf(tag) > -1) {
25958                 return;
25959             }
25960             if (this.cblack.indexOf(tag) > -1) {
25961                 return;
25962             }
25963             this.cblack.push(tag);
25964             
25965         }, this);
25966     },
25967     
25968     setStylesheets : function(stylesheets)
25969     {
25970         if(typeof(stylesheets) == 'string'){
25971             Roo.get(this.iframe.contentDocument.head).createChild({
25972                 tag : 'link',
25973                 rel : 'stylesheet',
25974                 type : 'text/css',
25975                 href : stylesheets
25976             });
25977             
25978             return;
25979         }
25980         var _this = this;
25981      
25982         Roo.each(stylesheets, function(s) {
25983             if(!s.length){
25984                 return;
25985             }
25986             
25987             Roo.get(_this.iframe.contentDocument.head).createChild({
25988                 tag : 'link',
25989                 rel : 'stylesheet',
25990                 type : 'text/css',
25991                 href : s
25992             });
25993         });
25994
25995         
25996     },
25997     
25998     removeStylesheets : function()
25999     {
26000         var _this = this;
26001         
26002         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26003             s.remove();
26004         });
26005     },
26006     
26007     setStyle : function(style)
26008     {
26009         Roo.get(this.iframe.contentDocument.head).createChild({
26010             tag : 'style',
26011             type : 'text/css',
26012             html : style
26013         });
26014
26015         return;
26016     }
26017     
26018     // hide stuff that is not compatible
26019     /**
26020      * @event blur
26021      * @hide
26022      */
26023     /**
26024      * @event change
26025      * @hide
26026      */
26027     /**
26028      * @event focus
26029      * @hide
26030      */
26031     /**
26032      * @event specialkey
26033      * @hide
26034      */
26035     /**
26036      * @cfg {String} fieldClass @hide
26037      */
26038     /**
26039      * @cfg {String} focusClass @hide
26040      */
26041     /**
26042      * @cfg {String} autoCreate @hide
26043      */
26044     /**
26045      * @cfg {String} inputType @hide
26046      */
26047     /**
26048      * @cfg {String} invalidClass @hide
26049      */
26050     /**
26051      * @cfg {String} invalidText @hide
26052      */
26053     /**
26054      * @cfg {String} msgFx @hide
26055      */
26056     /**
26057      * @cfg {String} validateOnBlur @hide
26058      */
26059 });
26060
26061 Roo.HtmlEditorCore.white = [
26062         'area', 'br', 'img', 'input', 'hr', 'wbr',
26063         
26064        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26065        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26066        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26067        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26068        'table',   'ul',         'xmp', 
26069        
26070        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26071       'thead',   'tr', 
26072      
26073       'dir', 'menu', 'ol', 'ul', 'dl',
26074        
26075       'embed',  'object'
26076 ];
26077
26078
26079 Roo.HtmlEditorCore.black = [
26080     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26081         'applet', // 
26082         'base',   'basefont', 'bgsound', 'blink',  'body', 
26083         'frame',  'frameset', 'head',    'html',   'ilayer', 
26084         'iframe', 'layer',  'link',     'meta',    'object',   
26085         'script', 'style' ,'title',  'xml' // clean later..
26086 ];
26087 Roo.HtmlEditorCore.clean = [
26088     'script', 'style', 'title', 'xml'
26089 ];
26090 Roo.HtmlEditorCore.remove = [
26091     'font'
26092 ];
26093 // attributes..
26094
26095 Roo.HtmlEditorCore.ablack = [
26096     'on'
26097 ];
26098     
26099 Roo.HtmlEditorCore.aclean = [ 
26100     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26101 ];
26102
26103 // protocols..
26104 Roo.HtmlEditorCore.pwhite= [
26105         'http',  'https',  'mailto'
26106 ];
26107
26108 // white listed style attributes.
26109 Roo.HtmlEditorCore.cwhite= [
26110       //  'text-align', /// default is to allow most things..
26111       
26112          
26113 //        'font-size'//??
26114 ];
26115
26116 // black listed style attributes.
26117 Roo.HtmlEditorCore.cblack= [
26118       //  'font-size' -- this can be set by the project 
26119 ];
26120
26121
26122 Roo.HtmlEditorCore.swapCodes   =[ 
26123     [    8211, "&#8211;" ], 
26124     [    8212, "&#8212;" ], 
26125     [    8216,  "'" ],  
26126     [    8217, "'" ],  
26127     [    8220, '"' ],  
26128     [    8221, '"' ],  
26129     [    8226, "*" ],  
26130     [    8230, "..." ]
26131 ]; 
26132
26133     /*
26134  * - LGPL
26135  *
26136  * HtmlEditor
26137  * 
26138  */
26139
26140 /**
26141  * @class Roo.bootstrap.HtmlEditor
26142  * @extends Roo.bootstrap.TextArea
26143  * Bootstrap HtmlEditor class
26144
26145  * @constructor
26146  * Create a new HtmlEditor
26147  * @param {Object} config The config object
26148  */
26149
26150 Roo.bootstrap.HtmlEditor = function(config){
26151     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26152     if (!this.toolbars) {
26153         this.toolbars = [];
26154     }
26155     
26156     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26157     this.addEvents({
26158             /**
26159              * @event initialize
26160              * Fires when the editor is fully initialized (including the iframe)
26161              * @param {HtmlEditor} this
26162              */
26163             initialize: true,
26164             /**
26165              * @event activate
26166              * Fires when the editor is first receives the focus. Any insertion must wait
26167              * until after this event.
26168              * @param {HtmlEditor} this
26169              */
26170             activate: true,
26171              /**
26172              * @event beforesync
26173              * Fires before the textarea is updated with content from the editor iframe. Return false
26174              * to cancel the sync.
26175              * @param {HtmlEditor} this
26176              * @param {String} html
26177              */
26178             beforesync: true,
26179              /**
26180              * @event beforepush
26181              * Fires before the iframe editor is updated with content from the textarea. Return false
26182              * to cancel the push.
26183              * @param {HtmlEditor} this
26184              * @param {String} html
26185              */
26186             beforepush: true,
26187              /**
26188              * @event sync
26189              * Fires when the textarea is updated with content from the editor iframe.
26190              * @param {HtmlEditor} this
26191              * @param {String} html
26192              */
26193             sync: true,
26194              /**
26195              * @event push
26196              * Fires when the iframe editor is updated with content from the textarea.
26197              * @param {HtmlEditor} this
26198              * @param {String} html
26199              */
26200             push: true,
26201              /**
26202              * @event editmodechange
26203              * Fires when the editor switches edit modes
26204              * @param {HtmlEditor} this
26205              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26206              */
26207             editmodechange: true,
26208             /**
26209              * @event editorevent
26210              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26211              * @param {HtmlEditor} this
26212              */
26213             editorevent: true,
26214             /**
26215              * @event firstfocus
26216              * Fires when on first focus - needed by toolbars..
26217              * @param {HtmlEditor} this
26218              */
26219             firstfocus: true,
26220             /**
26221              * @event autosave
26222              * Auto save the htmlEditor value as a file into Events
26223              * @param {HtmlEditor} this
26224              */
26225             autosave: true,
26226             /**
26227              * @event savedpreview
26228              * preview the saved version of htmlEditor
26229              * @param {HtmlEditor} this
26230              */
26231             savedpreview: true
26232         });
26233 };
26234
26235
26236 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26237     
26238     
26239       /**
26240      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26241      */
26242     toolbars : false,
26243     
26244      /**
26245     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26246     */
26247     btns : [],
26248    
26249      /**
26250      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26251      *                        Roo.resizable.
26252      */
26253     resizable : false,
26254      /**
26255      * @cfg {Number} height (in pixels)
26256      */   
26257     height: 300,
26258    /**
26259      * @cfg {Number} width (in pixels)
26260      */   
26261     width: false,
26262     
26263     /**
26264      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26265      * 
26266      */
26267     stylesheets: false,
26268     
26269     // id of frame..
26270     frameId: false,
26271     
26272     // private properties
26273     validationEvent : false,
26274     deferHeight: true,
26275     initialized : false,
26276     activated : false,
26277     
26278     onFocus : Roo.emptyFn,
26279     iframePad:3,
26280     hideMode:'offsets',
26281     
26282     tbContainer : false,
26283     
26284     bodyCls : '',
26285     
26286     toolbarContainer :function() {
26287         return this.wrap.select('.x-html-editor-tb',true).first();
26288     },
26289
26290     /**
26291      * Protected method that will not generally be called directly. It
26292      * is called when the editor creates its toolbar. Override this method if you need to
26293      * add custom toolbar buttons.
26294      * @param {HtmlEditor} editor
26295      */
26296     createToolbar : function(){
26297         Roo.log('renewing');
26298         Roo.log("create toolbars");
26299         
26300         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26301         this.toolbars[0].render(this.toolbarContainer());
26302         
26303         return;
26304         
26305 //        if (!editor.toolbars || !editor.toolbars.length) {
26306 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26307 //        }
26308 //        
26309 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26310 //            editor.toolbars[i] = Roo.factory(
26311 //                    typeof(editor.toolbars[i]) == 'string' ?
26312 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26313 //                Roo.bootstrap.HtmlEditor);
26314 //            editor.toolbars[i].init(editor);
26315 //        }
26316     },
26317
26318      
26319     // private
26320     onRender : function(ct, position)
26321     {
26322        // Roo.log("Call onRender: " + this.xtype);
26323         var _t = this;
26324         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26325       
26326         this.wrap = this.inputEl().wrap({
26327             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26328         });
26329         
26330         this.editorcore.onRender(ct, position);
26331          
26332         if (this.resizable) {
26333             this.resizeEl = new Roo.Resizable(this.wrap, {
26334                 pinned : true,
26335                 wrap: true,
26336                 dynamic : true,
26337                 minHeight : this.height,
26338                 height: this.height,
26339                 handles : this.resizable,
26340                 width: this.width,
26341                 listeners : {
26342                     resize : function(r, w, h) {
26343                         _t.onResize(w,h); // -something
26344                     }
26345                 }
26346             });
26347             
26348         }
26349         this.createToolbar(this);
26350        
26351         
26352         if(!this.width && this.resizable){
26353             this.setSize(this.wrap.getSize());
26354         }
26355         if (this.resizeEl) {
26356             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26357             // should trigger onReize..
26358         }
26359         
26360     },
26361
26362     // private
26363     onResize : function(w, h)
26364     {
26365         Roo.log('resize: ' +w + ',' + h );
26366         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26367         var ew = false;
26368         var eh = false;
26369         
26370         if(this.inputEl() ){
26371             if(typeof w == 'number'){
26372                 var aw = w - this.wrap.getFrameWidth('lr');
26373                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26374                 ew = aw;
26375             }
26376             if(typeof h == 'number'){
26377                  var tbh = -11;  // fixme it needs to tool bar size!
26378                 for (var i =0; i < this.toolbars.length;i++) {
26379                     // fixme - ask toolbars for heights?
26380                     tbh += this.toolbars[i].el.getHeight();
26381                     //if (this.toolbars[i].footer) {
26382                     //    tbh += this.toolbars[i].footer.el.getHeight();
26383                     //}
26384                 }
26385               
26386                 
26387                 
26388                 
26389                 
26390                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26391                 ah -= 5; // knock a few pixes off for look..
26392                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26393                 var eh = ah;
26394             }
26395         }
26396         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26397         this.editorcore.onResize(ew,eh);
26398         
26399     },
26400
26401     /**
26402      * Toggles the editor between standard and source edit mode.
26403      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26404      */
26405     toggleSourceEdit : function(sourceEditMode)
26406     {
26407         this.editorcore.toggleSourceEdit(sourceEditMode);
26408         
26409         if(this.editorcore.sourceEditMode){
26410             Roo.log('editor - showing textarea');
26411             
26412 //            Roo.log('in');
26413 //            Roo.log(this.syncValue());
26414             this.syncValue();
26415             this.inputEl().removeClass(['hide', 'x-hidden']);
26416             this.inputEl().dom.removeAttribute('tabIndex');
26417             this.inputEl().focus();
26418         }else{
26419             Roo.log('editor - hiding textarea');
26420 //            Roo.log('out')
26421 //            Roo.log(this.pushValue()); 
26422             this.pushValue();
26423             
26424             this.inputEl().addClass(['hide', 'x-hidden']);
26425             this.inputEl().dom.setAttribute('tabIndex', -1);
26426             //this.deferFocus();
26427         }
26428          
26429         if(this.resizable){
26430             this.setSize(this.wrap.getSize());
26431         }
26432         
26433         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26434     },
26435  
26436     // private (for BoxComponent)
26437     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26438
26439     // private (for BoxComponent)
26440     getResizeEl : function(){
26441         return this.wrap;
26442     },
26443
26444     // private (for BoxComponent)
26445     getPositionEl : function(){
26446         return this.wrap;
26447     },
26448
26449     // private
26450     initEvents : function(){
26451         this.originalValue = this.getValue();
26452     },
26453
26454 //    /**
26455 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26456 //     * @method
26457 //     */
26458 //    markInvalid : Roo.emptyFn,
26459 //    /**
26460 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26461 //     * @method
26462 //     */
26463 //    clearInvalid : Roo.emptyFn,
26464
26465     setValue : function(v){
26466         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26467         this.editorcore.pushValue();
26468     },
26469
26470      
26471     // private
26472     deferFocus : function(){
26473         this.focus.defer(10, this);
26474     },
26475
26476     // doc'ed in Field
26477     focus : function(){
26478         this.editorcore.focus();
26479         
26480     },
26481       
26482
26483     // private
26484     onDestroy : function(){
26485         
26486         
26487         
26488         if(this.rendered){
26489             
26490             for (var i =0; i < this.toolbars.length;i++) {
26491                 // fixme - ask toolbars for heights?
26492                 this.toolbars[i].onDestroy();
26493             }
26494             
26495             this.wrap.dom.innerHTML = '';
26496             this.wrap.remove();
26497         }
26498     },
26499
26500     // private
26501     onFirstFocus : function(){
26502         //Roo.log("onFirstFocus");
26503         this.editorcore.onFirstFocus();
26504          for (var i =0; i < this.toolbars.length;i++) {
26505             this.toolbars[i].onFirstFocus();
26506         }
26507         
26508     },
26509     
26510     // private
26511     syncValue : function()
26512     {   
26513         this.editorcore.syncValue();
26514     },
26515     
26516     pushValue : function()
26517     {   
26518         this.editorcore.pushValue();
26519     }
26520      
26521     
26522     // hide stuff that is not compatible
26523     /**
26524      * @event blur
26525      * @hide
26526      */
26527     /**
26528      * @event change
26529      * @hide
26530      */
26531     /**
26532      * @event focus
26533      * @hide
26534      */
26535     /**
26536      * @event specialkey
26537      * @hide
26538      */
26539     /**
26540      * @cfg {String} fieldClass @hide
26541      */
26542     /**
26543      * @cfg {String} focusClass @hide
26544      */
26545     /**
26546      * @cfg {String} autoCreate @hide
26547      */
26548     /**
26549      * @cfg {String} inputType @hide
26550      */
26551      
26552     /**
26553      * @cfg {String} invalidText @hide
26554      */
26555     /**
26556      * @cfg {String} msgFx @hide
26557      */
26558     /**
26559      * @cfg {String} validateOnBlur @hide
26560      */
26561 });
26562  
26563     
26564    
26565    
26566    
26567       
26568 Roo.namespace('Roo.bootstrap.htmleditor');
26569 /**
26570  * @class Roo.bootstrap.HtmlEditorToolbar1
26571  * Basic Toolbar
26572  * 
26573  * @example
26574  * Usage:
26575  *
26576  new Roo.bootstrap.HtmlEditor({
26577     ....
26578     toolbars : [
26579         new Roo.bootstrap.HtmlEditorToolbar1({
26580             disable : { fonts: 1 , format: 1, ..., ... , ...],
26581             btns : [ .... ]
26582         })
26583     }
26584      
26585  * 
26586  * @cfg {Object} disable List of elements to disable..
26587  * @cfg {Array} btns List of additional buttons.
26588  * 
26589  * 
26590  * NEEDS Extra CSS? 
26591  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26592  */
26593  
26594 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26595 {
26596     
26597     Roo.apply(this, config);
26598     
26599     // default disabled, based on 'good practice'..
26600     this.disable = this.disable || {};
26601     Roo.applyIf(this.disable, {
26602         fontSize : true,
26603         colors : true,
26604         specialElements : true
26605     });
26606     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26607     
26608     this.editor = config.editor;
26609     this.editorcore = config.editor.editorcore;
26610     
26611     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26612     
26613     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26614     // dont call parent... till later.
26615 }
26616 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26617      
26618     bar : true,
26619     
26620     editor : false,
26621     editorcore : false,
26622     
26623     
26624     formats : [
26625         "p" ,  
26626         "h1","h2","h3","h4","h5","h6", 
26627         "pre", "code", 
26628         "abbr", "acronym", "address", "cite", "samp", "var",
26629         'div','span'
26630     ],
26631     
26632     onRender : function(ct, position)
26633     {
26634        // Roo.log("Call onRender: " + this.xtype);
26635         
26636        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26637        Roo.log(this.el);
26638        this.el.dom.style.marginBottom = '0';
26639        var _this = this;
26640        var editorcore = this.editorcore;
26641        var editor= this.editor;
26642        
26643        var children = [];
26644        var btn = function(id,cmd , toggle, handler, html){
26645        
26646             var  event = toggle ? 'toggle' : 'click';
26647        
26648             var a = {
26649                 size : 'sm',
26650                 xtype: 'Button',
26651                 xns: Roo.bootstrap,
26652                 //glyphicon : id,
26653                 fa: id,
26654                 cmd : id || cmd,
26655                 enableToggle:toggle !== false,
26656                 html : html || '',
26657                 pressed : toggle ? false : null,
26658                 listeners : {}
26659             };
26660             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26661                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26662             };
26663             children.push(a);
26664             return a;
26665        }
26666        
26667     //    var cb_box = function...
26668         
26669         var style = {
26670                 xtype: 'Button',
26671                 size : 'sm',
26672                 xns: Roo.bootstrap,
26673                 fa : 'font',
26674                 //html : 'submit'
26675                 menu : {
26676                     xtype: 'Menu',
26677                     xns: Roo.bootstrap,
26678                     items:  []
26679                 }
26680         };
26681         Roo.each(this.formats, function(f) {
26682             style.menu.items.push({
26683                 xtype :'MenuItem',
26684                 xns: Roo.bootstrap,
26685                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26686                 tagname : f,
26687                 listeners : {
26688                     click : function()
26689                     {
26690                         editorcore.insertTag(this.tagname);
26691                         editor.focus();
26692                     }
26693                 }
26694                 
26695             });
26696         });
26697         children.push(style);   
26698         
26699         btn('bold',false,true);
26700         btn('italic',false,true);
26701         btn('align-left', 'justifyleft',true);
26702         btn('align-center', 'justifycenter',true);
26703         btn('align-right' , 'justifyright',true);
26704         btn('link', false, false, function(btn) {
26705             //Roo.log("create link?");
26706             var url = prompt(this.createLinkText, this.defaultLinkValue);
26707             if(url && url != 'http:/'+'/'){
26708                 this.editorcore.relayCmd('createlink', url);
26709             }
26710         }),
26711         btn('list','insertunorderedlist',true);
26712         btn('pencil', false,true, function(btn){
26713                 Roo.log(this);
26714                 this.toggleSourceEdit(btn.pressed);
26715         });
26716         
26717         if (this.editor.btns.length > 0) {
26718             for (var i = 0; i<this.editor.btns.length; i++) {
26719                 children.push(this.editor.btns[i]);
26720             }
26721         }
26722         
26723         /*
26724         var cog = {
26725                 xtype: 'Button',
26726                 size : 'sm',
26727                 xns: Roo.bootstrap,
26728                 glyphicon : 'cog',
26729                 //html : 'submit'
26730                 menu : {
26731                     xtype: 'Menu',
26732                     xns: Roo.bootstrap,
26733                     items:  []
26734                 }
26735         };
26736         
26737         cog.menu.items.push({
26738             xtype :'MenuItem',
26739             xns: Roo.bootstrap,
26740             html : Clean styles,
26741             tagname : f,
26742             listeners : {
26743                 click : function()
26744                 {
26745                     editorcore.insertTag(this.tagname);
26746                     editor.focus();
26747                 }
26748             }
26749             
26750         });
26751        */
26752         
26753          
26754        this.xtype = 'NavSimplebar';
26755         
26756         for(var i=0;i< children.length;i++) {
26757             
26758             this.buttons.add(this.addxtypeChild(children[i]));
26759             
26760         }
26761         
26762         editor.on('editorevent', this.updateToolbar, this);
26763     },
26764     onBtnClick : function(id)
26765     {
26766        this.editorcore.relayCmd(id);
26767        this.editorcore.focus();
26768     },
26769     
26770     /**
26771      * Protected method that will not generally be called directly. It triggers
26772      * a toolbar update by reading the markup state of the current selection in the editor.
26773      */
26774     updateToolbar: function(){
26775
26776         if(!this.editorcore.activated){
26777             this.editor.onFirstFocus(); // is this neeed?
26778             return;
26779         }
26780
26781         var btns = this.buttons; 
26782         var doc = this.editorcore.doc;
26783         btns.get('bold').setActive(doc.queryCommandState('bold'));
26784         btns.get('italic').setActive(doc.queryCommandState('italic'));
26785         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26786         
26787         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26788         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26789         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26790         
26791         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26792         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26793          /*
26794         
26795         var ans = this.editorcore.getAllAncestors();
26796         if (this.formatCombo) {
26797             
26798             
26799             var store = this.formatCombo.store;
26800             this.formatCombo.setValue("");
26801             for (var i =0; i < ans.length;i++) {
26802                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26803                     // select it..
26804                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26805                     break;
26806                 }
26807             }
26808         }
26809         
26810         
26811         
26812         // hides menus... - so this cant be on a menu...
26813         Roo.bootstrap.MenuMgr.hideAll();
26814         */
26815         Roo.bootstrap.MenuMgr.hideAll();
26816         //this.editorsyncValue();
26817     },
26818     onFirstFocus: function() {
26819         this.buttons.each(function(item){
26820            item.enable();
26821         });
26822     },
26823     toggleSourceEdit : function(sourceEditMode){
26824         
26825           
26826         if(sourceEditMode){
26827             Roo.log("disabling buttons");
26828            this.buttons.each( function(item){
26829                 if(item.cmd != 'pencil'){
26830                     item.disable();
26831                 }
26832             });
26833           
26834         }else{
26835             Roo.log("enabling buttons");
26836             if(this.editorcore.initialized){
26837                 this.buttons.each( function(item){
26838                     item.enable();
26839                 });
26840             }
26841             
26842         }
26843         Roo.log("calling toggole on editor");
26844         // tell the editor that it's been pressed..
26845         this.editor.toggleSourceEdit(sourceEditMode);
26846        
26847     }
26848 });
26849
26850
26851
26852
26853  
26854 /*
26855  * - LGPL
26856  */
26857
26858 /**
26859  * @class Roo.bootstrap.Markdown
26860  * @extends Roo.bootstrap.TextArea
26861  * Bootstrap Showdown editable area
26862  * @cfg {string} content
26863  * 
26864  * @constructor
26865  * Create a new Showdown
26866  */
26867
26868 Roo.bootstrap.Markdown = function(config){
26869     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26870    
26871 };
26872
26873 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26874     
26875     editing :false,
26876     
26877     initEvents : function()
26878     {
26879         
26880         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26881         this.markdownEl = this.el.createChild({
26882             cls : 'roo-markdown-area'
26883         });
26884         this.inputEl().addClass('d-none');
26885         if (this.getValue() == '') {
26886             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26887             
26888         } else {
26889             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26890         }
26891         this.markdownEl.on('click', this.toggleTextEdit, this);
26892         this.on('blur', this.toggleTextEdit, this);
26893         this.on('specialkey', this.resizeTextArea, this);
26894     },
26895     
26896     toggleTextEdit : function()
26897     {
26898         var sh = this.markdownEl.getHeight();
26899         this.inputEl().addClass('d-none');
26900         this.markdownEl.addClass('d-none');
26901         if (!this.editing) {
26902             // show editor?
26903             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26904             this.inputEl().removeClass('d-none');
26905             this.inputEl().focus();
26906             this.editing = true;
26907             return;
26908         }
26909         // show showdown...
26910         this.updateMarkdown();
26911         this.markdownEl.removeClass('d-none');
26912         this.editing = false;
26913         return;
26914     },
26915     updateMarkdown : function()
26916     {
26917         if (this.getValue() == '') {
26918             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26919             return;
26920         }
26921  
26922         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26923     },
26924     
26925     resizeTextArea: function () {
26926         
26927         var sh = 100;
26928         Roo.log([sh, this.getValue().split("\n").length * 30]);
26929         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26930     },
26931     setValue : function(val)
26932     {
26933         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26934         if (!this.editing) {
26935             this.updateMarkdown();
26936         }
26937         
26938     },
26939     focus : function()
26940     {
26941         if (!this.editing) {
26942             this.toggleTextEdit();
26943         }
26944         
26945     }
26946
26947
26948 });
26949 /**
26950  * @class Roo.bootstrap.Table.AbstractSelectionModel
26951  * @extends Roo.util.Observable
26952  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26953  * implemented by descendant classes.  This class should not be directly instantiated.
26954  * @constructor
26955  */
26956 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26957     this.locked = false;
26958     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26959 };
26960
26961
26962 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26963     /** @ignore Called by the grid automatically. Do not call directly. */
26964     init : function(grid){
26965         this.grid = grid;
26966         this.initEvents();
26967     },
26968
26969     /**
26970      * Locks the selections.
26971      */
26972     lock : function(){
26973         this.locked = true;
26974     },
26975
26976     /**
26977      * Unlocks the selections.
26978      */
26979     unlock : function(){
26980         this.locked = false;
26981     },
26982
26983     /**
26984      * Returns true if the selections are locked.
26985      * @return {Boolean}
26986      */
26987     isLocked : function(){
26988         return this.locked;
26989     },
26990     
26991     
26992     initEvents : function ()
26993     {
26994         
26995     }
26996 });
26997 /**
26998  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26999  * @class Roo.bootstrap.Table.RowSelectionModel
27000  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27001  * It supports multiple selections and keyboard selection/navigation. 
27002  * @constructor
27003  * @param {Object} config
27004  */
27005
27006 Roo.bootstrap.Table.RowSelectionModel = function(config){
27007     Roo.apply(this, config);
27008     this.selections = new Roo.util.MixedCollection(false, function(o){
27009         return o.id;
27010     });
27011
27012     this.last = false;
27013     this.lastActive = false;
27014
27015     this.addEvents({
27016         /**
27017              * @event selectionchange
27018              * Fires when the selection changes
27019              * @param {SelectionModel} this
27020              */
27021             "selectionchange" : true,
27022         /**
27023              * @event afterselectionchange
27024              * Fires after the selection changes (eg. by key press or clicking)
27025              * @param {SelectionModel} this
27026              */
27027             "afterselectionchange" : true,
27028         /**
27029              * @event beforerowselect
27030              * Fires when a row is selected being selected, return false to cancel.
27031              * @param {SelectionModel} this
27032              * @param {Number} rowIndex The selected index
27033              * @param {Boolean} keepExisting False if other selections will be cleared
27034              */
27035             "beforerowselect" : true,
27036         /**
27037              * @event rowselect
27038              * Fires when a row is selected.
27039              * @param {SelectionModel} this
27040              * @param {Number} rowIndex The selected index
27041              * @param {Roo.data.Record} r The record
27042              */
27043             "rowselect" : true,
27044         /**
27045              * @event rowdeselect
27046              * Fires when a row is deselected.
27047              * @param {SelectionModel} this
27048              * @param {Number} rowIndex The selected index
27049              */
27050         "rowdeselect" : true
27051     });
27052     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27053     this.locked = false;
27054  };
27055
27056 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27057     /**
27058      * @cfg {Boolean} singleSelect
27059      * True to allow selection of only one row at a time (defaults to false)
27060      */
27061     singleSelect : false,
27062
27063     // private
27064     initEvents : function()
27065     {
27066
27067         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27068         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27069         //}else{ // allow click to work like normal
27070          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27071         //}
27072         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27073         this.grid.on("rowclick", this.handleMouseDown, this);
27074         
27075         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27076             "up" : function(e){
27077                 if(!e.shiftKey){
27078                     this.selectPrevious(e.shiftKey);
27079                 }else if(this.last !== false && this.lastActive !== false){
27080                     var last = this.last;
27081                     this.selectRange(this.last,  this.lastActive-1);
27082                     this.grid.getView().focusRow(this.lastActive);
27083                     if(last !== false){
27084                         this.last = last;
27085                     }
27086                 }else{
27087                     this.selectFirstRow();
27088                 }
27089                 this.fireEvent("afterselectionchange", this);
27090             },
27091             "down" : function(e){
27092                 if(!e.shiftKey){
27093                     this.selectNext(e.shiftKey);
27094                 }else if(this.last !== false && this.lastActive !== false){
27095                     var last = this.last;
27096                     this.selectRange(this.last,  this.lastActive+1);
27097                     this.grid.getView().focusRow(this.lastActive);
27098                     if(last !== false){
27099                         this.last = last;
27100                     }
27101                 }else{
27102                     this.selectFirstRow();
27103                 }
27104                 this.fireEvent("afterselectionchange", this);
27105             },
27106             scope: this
27107         });
27108         this.grid.store.on('load', function(){
27109             this.selections.clear();
27110         },this);
27111         /*
27112         var view = this.grid.view;
27113         view.on("refresh", this.onRefresh, this);
27114         view.on("rowupdated", this.onRowUpdated, this);
27115         view.on("rowremoved", this.onRemove, this);
27116         */
27117     },
27118
27119     // private
27120     onRefresh : function()
27121     {
27122         var ds = this.grid.store, i, v = this.grid.view;
27123         var s = this.selections;
27124         s.each(function(r){
27125             if((i = ds.indexOfId(r.id)) != -1){
27126                 v.onRowSelect(i);
27127             }else{
27128                 s.remove(r);
27129             }
27130         });
27131     },
27132
27133     // private
27134     onRemove : function(v, index, r){
27135         this.selections.remove(r);
27136     },
27137
27138     // private
27139     onRowUpdated : function(v, index, r){
27140         if(this.isSelected(r)){
27141             v.onRowSelect(index);
27142         }
27143     },
27144
27145     /**
27146      * Select records.
27147      * @param {Array} records The records to select
27148      * @param {Boolean} keepExisting (optional) True to keep existing selections
27149      */
27150     selectRecords : function(records, keepExisting)
27151     {
27152         if(!keepExisting){
27153             this.clearSelections();
27154         }
27155             var ds = this.grid.store;
27156         for(var i = 0, len = records.length; i < len; i++){
27157             this.selectRow(ds.indexOf(records[i]), true);
27158         }
27159     },
27160
27161     /**
27162      * Gets the number of selected rows.
27163      * @return {Number}
27164      */
27165     getCount : function(){
27166         return this.selections.length;
27167     },
27168
27169     /**
27170      * Selects the first row in the grid.
27171      */
27172     selectFirstRow : function(){
27173         this.selectRow(0);
27174     },
27175
27176     /**
27177      * Select the last row.
27178      * @param {Boolean} keepExisting (optional) True to keep existing selections
27179      */
27180     selectLastRow : function(keepExisting){
27181         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27182         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27183     },
27184
27185     /**
27186      * Selects the row immediately following the last selected row.
27187      * @param {Boolean} keepExisting (optional) True to keep existing selections
27188      */
27189     selectNext : function(keepExisting)
27190     {
27191             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27192             this.selectRow(this.last+1, keepExisting);
27193             this.grid.getView().focusRow(this.last);
27194         }
27195     },
27196
27197     /**
27198      * Selects the row that precedes the last selected row.
27199      * @param {Boolean} keepExisting (optional) True to keep existing selections
27200      */
27201     selectPrevious : function(keepExisting){
27202         if(this.last){
27203             this.selectRow(this.last-1, keepExisting);
27204             this.grid.getView().focusRow(this.last);
27205         }
27206     },
27207
27208     /**
27209      * Returns the selected records
27210      * @return {Array} Array of selected records
27211      */
27212     getSelections : function(){
27213         return [].concat(this.selections.items);
27214     },
27215
27216     /**
27217      * Returns the first selected record.
27218      * @return {Record}
27219      */
27220     getSelected : function(){
27221         return this.selections.itemAt(0);
27222     },
27223
27224
27225     /**
27226      * Clears all selections.
27227      */
27228     clearSelections : function(fast)
27229     {
27230         if(this.locked) {
27231             return;
27232         }
27233         if(fast !== true){
27234                 var ds = this.grid.store;
27235             var s = this.selections;
27236             s.each(function(r){
27237                 this.deselectRow(ds.indexOfId(r.id));
27238             }, this);
27239             s.clear();
27240         }else{
27241             this.selections.clear();
27242         }
27243         this.last = false;
27244     },
27245
27246
27247     /**
27248      * Selects all rows.
27249      */
27250     selectAll : function(){
27251         if(this.locked) {
27252             return;
27253         }
27254         this.selections.clear();
27255         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27256             this.selectRow(i, true);
27257         }
27258     },
27259
27260     /**
27261      * Returns True if there is a selection.
27262      * @return {Boolean}
27263      */
27264     hasSelection : function(){
27265         return this.selections.length > 0;
27266     },
27267
27268     /**
27269      * Returns True if the specified row is selected.
27270      * @param {Number/Record} record The record or index of the record to check
27271      * @return {Boolean}
27272      */
27273     isSelected : function(index){
27274             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27275         return (r && this.selections.key(r.id) ? true : false);
27276     },
27277
27278     /**
27279      * Returns True if the specified record id is selected.
27280      * @param {String} id The id of record to check
27281      * @return {Boolean}
27282      */
27283     isIdSelected : function(id){
27284         return (this.selections.key(id) ? true : false);
27285     },
27286
27287
27288     // private
27289     handleMouseDBClick : function(e, t){
27290         
27291     },
27292     // private
27293     handleMouseDown : function(e, t)
27294     {
27295             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27296         if(this.isLocked() || rowIndex < 0 ){
27297             return;
27298         };
27299         if(e.shiftKey && this.last !== false){
27300             var last = this.last;
27301             this.selectRange(last, rowIndex, e.ctrlKey);
27302             this.last = last; // reset the last
27303             t.focus();
27304     
27305         }else{
27306             var isSelected = this.isSelected(rowIndex);
27307             //Roo.log("select row:" + rowIndex);
27308             if(isSelected){
27309                 this.deselectRow(rowIndex);
27310             } else {
27311                         this.selectRow(rowIndex, true);
27312             }
27313     
27314             /*
27315                 if(e.button !== 0 && isSelected){
27316                 alert('rowIndex 2: ' + rowIndex);
27317                     view.focusRow(rowIndex);
27318                 }else if(e.ctrlKey && isSelected){
27319                     this.deselectRow(rowIndex);
27320                 }else if(!isSelected){
27321                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27322                     view.focusRow(rowIndex);
27323                 }
27324             */
27325         }
27326         this.fireEvent("afterselectionchange", this);
27327     },
27328     // private
27329     handleDragableRowClick :  function(grid, rowIndex, e) 
27330     {
27331         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27332             this.selectRow(rowIndex, false);
27333             grid.view.focusRow(rowIndex);
27334              this.fireEvent("afterselectionchange", this);
27335         }
27336     },
27337     
27338     /**
27339      * Selects multiple rows.
27340      * @param {Array} rows Array of the indexes of the row to select
27341      * @param {Boolean} keepExisting (optional) True to keep existing selections
27342      */
27343     selectRows : function(rows, keepExisting){
27344         if(!keepExisting){
27345             this.clearSelections();
27346         }
27347         for(var i = 0, len = rows.length; i < len; i++){
27348             this.selectRow(rows[i], true);
27349         }
27350     },
27351
27352     /**
27353      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27354      * @param {Number} startRow The index of the first row in the range
27355      * @param {Number} endRow The index of the last row in the range
27356      * @param {Boolean} keepExisting (optional) True to retain existing selections
27357      */
27358     selectRange : function(startRow, endRow, keepExisting){
27359         if(this.locked) {
27360             return;
27361         }
27362         if(!keepExisting){
27363             this.clearSelections();
27364         }
27365         if(startRow <= endRow){
27366             for(var i = startRow; i <= endRow; i++){
27367                 this.selectRow(i, true);
27368             }
27369         }else{
27370             for(var i = startRow; i >= endRow; i--){
27371                 this.selectRow(i, true);
27372             }
27373         }
27374     },
27375
27376     /**
27377      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
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      */
27381     deselectRange : function(startRow, endRow, preventViewNotify){
27382         if(this.locked) {
27383             return;
27384         }
27385         for(var i = startRow; i <= endRow; i++){
27386             this.deselectRow(i, preventViewNotify);
27387         }
27388     },
27389
27390     /**
27391      * Selects a row.
27392      * @param {Number} row The index of the row to select
27393      * @param {Boolean} keepExisting (optional) True to keep existing selections
27394      */
27395     selectRow : function(index, keepExisting, preventViewNotify)
27396     {
27397             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27398             return;
27399         }
27400         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27401             if(!keepExisting || this.singleSelect){
27402                 this.clearSelections();
27403             }
27404             
27405             var r = this.grid.store.getAt(index);
27406             //console.log('selectRow - record id :' + r.id);
27407             
27408             this.selections.add(r);
27409             this.last = this.lastActive = index;
27410             if(!preventViewNotify){
27411                 var proxy = new Roo.Element(
27412                                 this.grid.getRowDom(index)
27413                 );
27414                 proxy.addClass('bg-info info');
27415             }
27416             this.fireEvent("rowselect", this, index, r);
27417             this.fireEvent("selectionchange", this);
27418         }
27419     },
27420
27421     /**
27422      * Deselects a row.
27423      * @param {Number} row The index of the row to deselect
27424      */
27425     deselectRow : function(index, preventViewNotify)
27426     {
27427         if(this.locked) {
27428             return;
27429         }
27430         if(this.last == index){
27431             this.last = false;
27432         }
27433         if(this.lastActive == index){
27434             this.lastActive = false;
27435         }
27436         
27437         var r = this.grid.store.getAt(index);
27438         if (!r) {
27439             return;
27440         }
27441         
27442         this.selections.remove(r);
27443         //.console.log('deselectRow - record id :' + r.id);
27444         if(!preventViewNotify){
27445         
27446             var proxy = new Roo.Element(
27447                 this.grid.getRowDom(index)
27448             );
27449             proxy.removeClass('bg-info info');
27450         }
27451         this.fireEvent("rowdeselect", this, index);
27452         this.fireEvent("selectionchange", this);
27453     },
27454
27455     // private
27456     restoreLast : function(){
27457         if(this._last){
27458             this.last = this._last;
27459         }
27460     },
27461
27462     // private
27463     acceptsNav : function(row, col, cm){
27464         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27465     },
27466
27467     // private
27468     onEditorKey : function(field, e){
27469         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27470         if(k == e.TAB){
27471             e.stopEvent();
27472             ed.completeEdit();
27473             if(e.shiftKey){
27474                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27475             }else{
27476                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27477             }
27478         }else if(k == e.ENTER && !e.ctrlKey){
27479             e.stopEvent();
27480             ed.completeEdit();
27481             if(e.shiftKey){
27482                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27483             }else{
27484                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27485             }
27486         }else if(k == e.ESC){
27487             ed.cancelEdit();
27488         }
27489         if(newCell){
27490             g.startEditing(newCell[0], newCell[1]);
27491         }
27492     }
27493 });
27494 /*
27495  * Based on:
27496  * Ext JS Library 1.1.1
27497  * Copyright(c) 2006-2007, Ext JS, LLC.
27498  *
27499  * Originally Released Under LGPL - original licence link has changed is not relivant.
27500  *
27501  * Fork - LGPL
27502  * <script type="text/javascript">
27503  */
27504  
27505 /**
27506  * @class Roo.bootstrap.PagingToolbar
27507  * @extends Roo.bootstrap.NavSimplebar
27508  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27509  * @constructor
27510  * Create a new PagingToolbar
27511  * @param {Object} config The config object
27512  * @param {Roo.data.Store} store
27513  */
27514 Roo.bootstrap.PagingToolbar = function(config)
27515 {
27516     // old args format still supported... - xtype is prefered..
27517         // created from xtype...
27518     
27519     this.ds = config.dataSource;
27520     
27521     if (config.store && !this.ds) {
27522         this.store= Roo.factory(config.store, Roo.data);
27523         this.ds = this.store;
27524         this.ds.xmodule = this.xmodule || false;
27525     }
27526     
27527     this.toolbarItems = [];
27528     if (config.items) {
27529         this.toolbarItems = config.items;
27530     }
27531     
27532     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27533     
27534     this.cursor = 0;
27535     
27536     if (this.ds) { 
27537         this.bind(this.ds);
27538     }
27539     
27540     if (Roo.bootstrap.version == 4) {
27541         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27542     } else {
27543         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27544     }
27545     
27546 };
27547
27548 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27549     /**
27550      * @cfg {Roo.data.Store} dataSource
27551      * The underlying data store providing the paged data
27552      */
27553     /**
27554      * @cfg {String/HTMLElement/Element} container
27555      * container The id or element that will contain the toolbar
27556      */
27557     /**
27558      * @cfg {Boolean} displayInfo
27559      * True to display the displayMsg (defaults to false)
27560      */
27561     /**
27562      * @cfg {Number} pageSize
27563      * The number of records to display per page (defaults to 20)
27564      */
27565     pageSize: 20,
27566     /**
27567      * @cfg {String} displayMsg
27568      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27569      */
27570     displayMsg : 'Displaying {0} - {1} of {2}',
27571     /**
27572      * @cfg {String} emptyMsg
27573      * The message to display when no records are found (defaults to "No data to display")
27574      */
27575     emptyMsg : 'No data to display',
27576     /**
27577      * Customizable piece of the default paging text (defaults to "Page")
27578      * @type String
27579      */
27580     beforePageText : "Page",
27581     /**
27582      * Customizable piece of the default paging text (defaults to "of %0")
27583      * @type String
27584      */
27585     afterPageText : "of {0}",
27586     /**
27587      * Customizable piece of the default paging text (defaults to "First Page")
27588      * @type String
27589      */
27590     firstText : "First Page",
27591     /**
27592      * Customizable piece of the default paging text (defaults to "Previous Page")
27593      * @type String
27594      */
27595     prevText : "Previous Page",
27596     /**
27597      * Customizable piece of the default paging text (defaults to "Next Page")
27598      * @type String
27599      */
27600     nextText : "Next Page",
27601     /**
27602      * Customizable piece of the default paging text (defaults to "Last Page")
27603      * @type String
27604      */
27605     lastText : "Last Page",
27606     /**
27607      * Customizable piece of the default paging text (defaults to "Refresh")
27608      * @type String
27609      */
27610     refreshText : "Refresh",
27611
27612     buttons : false,
27613     // private
27614     onRender : function(ct, position) 
27615     {
27616         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27617         this.navgroup.parentId = this.id;
27618         this.navgroup.onRender(this.el, null);
27619         // add the buttons to the navgroup
27620         
27621         if(this.displayInfo){
27622             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27623             this.displayEl = this.el.select('.x-paging-info', true).first();
27624 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27625 //            this.displayEl = navel.el.select('span',true).first();
27626         }
27627         
27628         var _this = this;
27629         
27630         if(this.buttons){
27631             Roo.each(_this.buttons, function(e){ // this might need to use render????
27632                Roo.factory(e).render(_this.el);
27633             });
27634         }
27635             
27636         Roo.each(_this.toolbarItems, function(e) {
27637             _this.navgroup.addItem(e);
27638         });
27639         
27640         
27641         this.first = this.navgroup.addItem({
27642             tooltip: this.firstText,
27643             cls: "prev btn-outline-secondary",
27644             html : ' <i class="fa fa-step-backward"></i>',
27645             disabled: true,
27646             preventDefault: true,
27647             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27648         });
27649         
27650         this.prev =  this.navgroup.addItem({
27651             tooltip: this.prevText,
27652             cls: "prev btn-outline-secondary",
27653             html : ' <i class="fa fa-backward"></i>',
27654             disabled: true,
27655             preventDefault: true,
27656             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27657         });
27658     //this.addSeparator();
27659         
27660         
27661         var field = this.navgroup.addItem( {
27662             tagtype : 'span',
27663             cls : 'x-paging-position  btn-outline-secondary',
27664              disabled: true,
27665             html : this.beforePageText  +
27666                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27667                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27668          } ); //?? escaped?
27669         
27670         this.field = field.el.select('input', true).first();
27671         this.field.on("keydown", this.onPagingKeydown, this);
27672         this.field.on("focus", function(){this.dom.select();});
27673     
27674     
27675         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27676         //this.field.setHeight(18);
27677         //this.addSeparator();
27678         this.next = this.navgroup.addItem({
27679             tooltip: this.nextText,
27680             cls: "next btn-outline-secondary",
27681             html : ' <i class="fa fa-forward"></i>',
27682             disabled: true,
27683             preventDefault: true,
27684             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27685         });
27686         this.last = this.navgroup.addItem({
27687             tooltip: this.lastText,
27688             html : ' <i class="fa fa-step-forward"></i>',
27689             cls: "next btn-outline-secondary",
27690             disabled: true,
27691             preventDefault: true,
27692             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27693         });
27694     //this.addSeparator();
27695         this.loading = this.navgroup.addItem({
27696             tooltip: this.refreshText,
27697             cls: "btn-outline-secondary",
27698             html : ' <i class="fa fa-refresh"></i>',
27699             preventDefault: true,
27700             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27701         });
27702         
27703     },
27704
27705     // private
27706     updateInfo : function(){
27707         if(this.displayEl){
27708             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27709             var msg = count == 0 ?
27710                 this.emptyMsg :
27711                 String.format(
27712                     this.displayMsg,
27713                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27714                 );
27715             this.displayEl.update(msg);
27716         }
27717     },
27718
27719     // private
27720     onLoad : function(ds, r, o)
27721     {
27722         this.cursor = o.params && o.params.start ? o.params.start : 0;
27723         
27724         var d = this.getPageData(),
27725             ap = d.activePage,
27726             ps = d.pages;
27727         
27728         
27729         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27730         this.field.dom.value = ap;
27731         this.first.setDisabled(ap == 1);
27732         this.prev.setDisabled(ap == 1);
27733         this.next.setDisabled(ap == ps);
27734         this.last.setDisabled(ap == ps);
27735         this.loading.enable();
27736         this.updateInfo();
27737     },
27738
27739     // private
27740     getPageData : function(){
27741         var total = this.ds.getTotalCount();
27742         return {
27743             total : total,
27744             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27745             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27746         };
27747     },
27748
27749     // private
27750     onLoadError : function(){
27751         this.loading.enable();
27752     },
27753
27754     // private
27755     onPagingKeydown : function(e){
27756         var k = e.getKey();
27757         var d = this.getPageData();
27758         if(k == e.RETURN){
27759             var v = this.field.dom.value, pageNum;
27760             if(!v || isNaN(pageNum = parseInt(v, 10))){
27761                 this.field.dom.value = d.activePage;
27762                 return;
27763             }
27764             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27765             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27766             e.stopEvent();
27767         }
27768         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))
27769         {
27770           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27771           this.field.dom.value = pageNum;
27772           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27773           e.stopEvent();
27774         }
27775         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27776         {
27777           var v = this.field.dom.value, pageNum; 
27778           var increment = (e.shiftKey) ? 10 : 1;
27779           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27780                 increment *= -1;
27781           }
27782           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27783             this.field.dom.value = d.activePage;
27784             return;
27785           }
27786           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27787           {
27788             this.field.dom.value = parseInt(v, 10) + increment;
27789             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27790             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27791           }
27792           e.stopEvent();
27793         }
27794     },
27795
27796     // private
27797     beforeLoad : function(){
27798         if(this.loading){
27799             this.loading.disable();
27800         }
27801     },
27802
27803     // private
27804     onClick : function(which){
27805         
27806         var ds = this.ds;
27807         if (!ds) {
27808             return;
27809         }
27810         
27811         switch(which){
27812             case "first":
27813                 ds.load({params:{start: 0, limit: this.pageSize}});
27814             break;
27815             case "prev":
27816                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27817             break;
27818             case "next":
27819                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27820             break;
27821             case "last":
27822                 var total = ds.getTotalCount();
27823                 var extra = total % this.pageSize;
27824                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27825                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27826             break;
27827             case "refresh":
27828                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27829             break;
27830         }
27831     },
27832
27833     /**
27834      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27835      * @param {Roo.data.Store} store The data store to unbind
27836      */
27837     unbind : function(ds){
27838         ds.un("beforeload", this.beforeLoad, this);
27839         ds.un("load", this.onLoad, this);
27840         ds.un("loadexception", this.onLoadError, this);
27841         ds.un("remove", this.updateInfo, this);
27842         ds.un("add", this.updateInfo, this);
27843         this.ds = undefined;
27844     },
27845
27846     /**
27847      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27848      * @param {Roo.data.Store} store The data store to bind
27849      */
27850     bind : function(ds){
27851         ds.on("beforeload", this.beforeLoad, this);
27852         ds.on("load", this.onLoad, this);
27853         ds.on("loadexception", this.onLoadError, this);
27854         ds.on("remove", this.updateInfo, this);
27855         ds.on("add", this.updateInfo, this);
27856         this.ds = ds;
27857     }
27858 });/*
27859  * - LGPL
27860  *
27861  * element
27862  * 
27863  */
27864
27865 /**
27866  * @class Roo.bootstrap.MessageBar
27867  * @extends Roo.bootstrap.Component
27868  * Bootstrap MessageBar class
27869  * @cfg {String} html contents of the MessageBar
27870  * @cfg {String} weight (info | success | warning | danger) default info
27871  * @cfg {String} beforeClass insert the bar before the given class
27872  * @cfg {Boolean} closable (true | false) default false
27873  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27874  * 
27875  * @constructor
27876  * Create a new Element
27877  * @param {Object} config The config object
27878  */
27879
27880 Roo.bootstrap.MessageBar = function(config){
27881     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27882 };
27883
27884 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27885     
27886     html: '',
27887     weight: 'info',
27888     closable: false,
27889     fixed: false,
27890     beforeClass: 'bootstrap-sticky-wrap',
27891     
27892     getAutoCreate : function(){
27893         
27894         var cfg = {
27895             tag: 'div',
27896             cls: 'alert alert-dismissable alert-' + this.weight,
27897             cn: [
27898                 {
27899                     tag: 'span',
27900                     cls: 'message',
27901                     html: this.html || ''
27902                 }
27903             ]
27904         };
27905         
27906         if(this.fixed){
27907             cfg.cls += ' alert-messages-fixed';
27908         }
27909         
27910         if(this.closable){
27911             cfg.cn.push({
27912                 tag: 'button',
27913                 cls: 'close',
27914                 html: 'x'
27915             });
27916         }
27917         
27918         return cfg;
27919     },
27920     
27921     onRender : function(ct, position)
27922     {
27923         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27924         
27925         if(!this.el){
27926             var cfg = Roo.apply({},  this.getAutoCreate());
27927             cfg.id = Roo.id();
27928             
27929             if (this.cls) {
27930                 cfg.cls += ' ' + this.cls;
27931             }
27932             if (this.style) {
27933                 cfg.style = this.style;
27934             }
27935             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27936             
27937             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27938         }
27939         
27940         this.el.select('>button.close').on('click', this.hide, this);
27941         
27942     },
27943     
27944     show : function()
27945     {
27946         if (!this.rendered) {
27947             this.render();
27948         }
27949         
27950         this.el.show();
27951         
27952         this.fireEvent('show', this);
27953         
27954     },
27955     
27956     hide : function()
27957     {
27958         if (!this.rendered) {
27959             this.render();
27960         }
27961         
27962         this.el.hide();
27963         
27964         this.fireEvent('hide', this);
27965     },
27966     
27967     update : function()
27968     {
27969 //        var e = this.el.dom.firstChild;
27970 //        
27971 //        if(this.closable){
27972 //            e = e.nextSibling;
27973 //        }
27974 //        
27975 //        e.data = this.html || '';
27976
27977         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27978     }
27979    
27980 });
27981
27982  
27983
27984      /*
27985  * - LGPL
27986  *
27987  * Graph
27988  * 
27989  */
27990
27991
27992 /**
27993  * @class Roo.bootstrap.Graph
27994  * @extends Roo.bootstrap.Component
27995  * Bootstrap Graph class
27996 > Prameters
27997  -sm {number} sm 4
27998  -md {number} md 5
27999  @cfg {String} graphtype  bar | vbar | pie
28000  @cfg {number} g_x coodinator | centre x (pie)
28001  @cfg {number} g_y coodinator | centre y (pie)
28002  @cfg {number} g_r radius (pie)
28003  @cfg {number} g_height height of the chart (respected by all elements in the set)
28004  @cfg {number} g_width width of the chart (respected by all elements in the set)
28005  @cfg {Object} title The title of the chart
28006     
28007  -{Array}  values
28008  -opts (object) options for the chart 
28009      o {
28010      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28011      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28012      o vgutter (number)
28013      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.
28014      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28015      o to
28016      o stretch (boolean)
28017      o }
28018  -opts (object) options for the pie
28019      o{
28020      o cut
28021      o startAngle (number)
28022      o endAngle (number)
28023      } 
28024  *
28025  * @constructor
28026  * Create a new Input
28027  * @param {Object} config The config object
28028  */
28029
28030 Roo.bootstrap.Graph = function(config){
28031     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28032     
28033     this.addEvents({
28034         // img events
28035         /**
28036          * @event click
28037          * The img click event for the img.
28038          * @param {Roo.EventObject} e
28039          */
28040         "click" : true
28041     });
28042 };
28043
28044 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28045     
28046     sm: 4,
28047     md: 5,
28048     graphtype: 'bar',
28049     g_height: 250,
28050     g_width: 400,
28051     g_x: 50,
28052     g_y: 50,
28053     g_r: 30,
28054     opts:{
28055         //g_colors: this.colors,
28056         g_type: 'soft',
28057         g_gutter: '20%'
28058
28059     },
28060     title : false,
28061
28062     getAutoCreate : function(){
28063         
28064         var cfg = {
28065             tag: 'div',
28066             html : null
28067         };
28068         
28069         
28070         return  cfg;
28071     },
28072
28073     onRender : function(ct,position){
28074         
28075         
28076         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28077         
28078         if (typeof(Raphael) == 'undefined') {
28079             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28080             return;
28081         }
28082         
28083         this.raphael = Raphael(this.el.dom);
28084         
28085                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28086                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28087                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28088                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28089                 /*
28090                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28091                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28092                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28093                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28094                 
28095                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28096                 r.barchart(330, 10, 300, 220, data1);
28097                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28098                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28099                 */
28100                 
28101                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28102                 // r.barchart(30, 30, 560, 250,  xdata, {
28103                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28104                 //     axis : "0 0 1 1",
28105                 //     axisxlabels :  xdata
28106                 //     //yvalues : cols,
28107                    
28108                 // });
28109 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28110 //        
28111 //        this.load(null,xdata,{
28112 //                axis : "0 0 1 1",
28113 //                axisxlabels :  xdata
28114 //                });
28115
28116     },
28117
28118     load : function(graphtype,xdata,opts)
28119     {
28120         this.raphael.clear();
28121         if(!graphtype) {
28122             graphtype = this.graphtype;
28123         }
28124         if(!opts){
28125             opts = this.opts;
28126         }
28127         var r = this.raphael,
28128             fin = function () {
28129                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28130             },
28131             fout = function () {
28132                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28133             },
28134             pfin = function() {
28135                 this.sector.stop();
28136                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28137
28138                 if (this.label) {
28139                     this.label[0].stop();
28140                     this.label[0].attr({ r: 7.5 });
28141                     this.label[1].attr({ "font-weight": 800 });
28142                 }
28143             },
28144             pfout = function() {
28145                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28146
28147                 if (this.label) {
28148                     this.label[0].animate({ r: 5 }, 500, "bounce");
28149                     this.label[1].attr({ "font-weight": 400 });
28150                 }
28151             };
28152
28153         switch(graphtype){
28154             case 'bar':
28155                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28156                 break;
28157             case 'hbar':
28158                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28159                 break;
28160             case 'pie':
28161 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28162 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28163 //            
28164                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28165                 
28166                 break;
28167
28168         }
28169         
28170         if(this.title){
28171             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28172         }
28173         
28174     },
28175     
28176     setTitle: function(o)
28177     {
28178         this.title = o;
28179     },
28180     
28181     initEvents: function() {
28182         
28183         if(!this.href){
28184             this.el.on('click', this.onClick, this);
28185         }
28186     },
28187     
28188     onClick : function(e)
28189     {
28190         Roo.log('img onclick');
28191         this.fireEvent('click', this, e);
28192     }
28193    
28194 });
28195
28196  
28197 /*
28198  * - LGPL
28199  *
28200  * numberBox
28201  * 
28202  */
28203 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28204
28205 /**
28206  * @class Roo.bootstrap.dash.NumberBox
28207  * @extends Roo.bootstrap.Component
28208  * Bootstrap NumberBox class
28209  * @cfg {String} headline Box headline
28210  * @cfg {String} content Box content
28211  * @cfg {String} icon Box icon
28212  * @cfg {String} footer Footer text
28213  * @cfg {String} fhref Footer href
28214  * 
28215  * @constructor
28216  * Create a new NumberBox
28217  * @param {Object} config The config object
28218  */
28219
28220
28221 Roo.bootstrap.dash.NumberBox = function(config){
28222     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28223     
28224 };
28225
28226 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28227     
28228     headline : '',
28229     content : '',
28230     icon : '',
28231     footer : '',
28232     fhref : '',
28233     ficon : '',
28234     
28235     getAutoCreate : function(){
28236         
28237         var cfg = {
28238             tag : 'div',
28239             cls : 'small-box ',
28240             cn : [
28241                 {
28242                     tag : 'div',
28243                     cls : 'inner',
28244                     cn :[
28245                         {
28246                             tag : 'h3',
28247                             cls : 'roo-headline',
28248                             html : this.headline
28249                         },
28250                         {
28251                             tag : 'p',
28252                             cls : 'roo-content',
28253                             html : this.content
28254                         }
28255                     ]
28256                 }
28257             ]
28258         };
28259         
28260         if(this.icon){
28261             cfg.cn.push({
28262                 tag : 'div',
28263                 cls : 'icon',
28264                 cn :[
28265                     {
28266                         tag : 'i',
28267                         cls : 'ion ' + this.icon
28268                     }
28269                 ]
28270             });
28271         }
28272         
28273         if(this.footer){
28274             var footer = {
28275                 tag : 'a',
28276                 cls : 'small-box-footer',
28277                 href : this.fhref || '#',
28278                 html : this.footer
28279             };
28280             
28281             cfg.cn.push(footer);
28282             
28283         }
28284         
28285         return  cfg;
28286     },
28287
28288     onRender : function(ct,position){
28289         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28290
28291
28292        
28293                 
28294     },
28295
28296     setHeadline: function (value)
28297     {
28298         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28299     },
28300     
28301     setFooter: function (value, href)
28302     {
28303         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28304         
28305         if(href){
28306             this.el.select('a.small-box-footer',true).first().attr('href', href);
28307         }
28308         
28309     },
28310
28311     setContent: function (value)
28312     {
28313         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28314     },
28315
28316     initEvents: function() 
28317     {   
28318         
28319     }
28320     
28321 });
28322
28323  
28324 /*
28325  * - LGPL
28326  *
28327  * TabBox
28328  * 
28329  */
28330 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28331
28332 /**
28333  * @class Roo.bootstrap.dash.TabBox
28334  * @extends Roo.bootstrap.Component
28335  * Bootstrap TabBox class
28336  * @cfg {String} title Title of the TabBox
28337  * @cfg {String} icon Icon of the TabBox
28338  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28339  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28340  * 
28341  * @constructor
28342  * Create a new TabBox
28343  * @param {Object} config The config object
28344  */
28345
28346
28347 Roo.bootstrap.dash.TabBox = function(config){
28348     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28349     this.addEvents({
28350         // raw events
28351         /**
28352          * @event addpane
28353          * When a pane is added
28354          * @param {Roo.bootstrap.dash.TabPane} pane
28355          */
28356         "addpane" : true,
28357         /**
28358          * @event activatepane
28359          * When a pane is activated
28360          * @param {Roo.bootstrap.dash.TabPane} pane
28361          */
28362         "activatepane" : true
28363         
28364          
28365     });
28366     
28367     this.panes = [];
28368 };
28369
28370 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28371
28372     title : '',
28373     icon : false,
28374     showtabs : true,
28375     tabScrollable : false,
28376     
28377     getChildContainer : function()
28378     {
28379         return this.el.select('.tab-content', true).first();
28380     },
28381     
28382     getAutoCreate : function(){
28383         
28384         var header = {
28385             tag: 'li',
28386             cls: 'pull-left header',
28387             html: this.title,
28388             cn : []
28389         };
28390         
28391         if(this.icon){
28392             header.cn.push({
28393                 tag: 'i',
28394                 cls: 'fa ' + this.icon
28395             });
28396         }
28397         
28398         var h = {
28399             tag: 'ul',
28400             cls: 'nav nav-tabs pull-right',
28401             cn: [
28402                 header
28403             ]
28404         };
28405         
28406         if(this.tabScrollable){
28407             h = {
28408                 tag: 'div',
28409                 cls: 'tab-header',
28410                 cn: [
28411                     {
28412                         tag: 'ul',
28413                         cls: 'nav nav-tabs pull-right',
28414                         cn: [
28415                             header
28416                         ]
28417                     }
28418                 ]
28419             };
28420         }
28421         
28422         var cfg = {
28423             tag: 'div',
28424             cls: 'nav-tabs-custom',
28425             cn: [
28426                 h,
28427                 {
28428                     tag: 'div',
28429                     cls: 'tab-content no-padding',
28430                     cn: []
28431                 }
28432             ]
28433         };
28434
28435         return  cfg;
28436     },
28437     initEvents : function()
28438     {
28439         //Roo.log('add add pane handler');
28440         this.on('addpane', this.onAddPane, this);
28441     },
28442      /**
28443      * Updates the box title
28444      * @param {String} html to set the title to.
28445      */
28446     setTitle : function(value)
28447     {
28448         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28449     },
28450     onAddPane : function(pane)
28451     {
28452         this.panes.push(pane);
28453         //Roo.log('addpane');
28454         //Roo.log(pane);
28455         // tabs are rendere left to right..
28456         if(!this.showtabs){
28457             return;
28458         }
28459         
28460         var ctr = this.el.select('.nav-tabs', true).first();
28461          
28462          
28463         var existing = ctr.select('.nav-tab',true);
28464         var qty = existing.getCount();;
28465         
28466         
28467         var tab = ctr.createChild({
28468             tag : 'li',
28469             cls : 'nav-tab' + (qty ? '' : ' active'),
28470             cn : [
28471                 {
28472                     tag : 'a',
28473                     href:'#',
28474                     html : pane.title
28475                 }
28476             ]
28477         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28478         pane.tab = tab;
28479         
28480         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28481         if (!qty) {
28482             pane.el.addClass('active');
28483         }
28484         
28485                 
28486     },
28487     onTabClick : function(ev,un,ob,pane)
28488     {
28489         //Roo.log('tab - prev default');
28490         ev.preventDefault();
28491         
28492         
28493         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28494         pane.tab.addClass('active');
28495         //Roo.log(pane.title);
28496         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28497         // technically we should have a deactivate event.. but maybe add later.
28498         // and it should not de-activate the selected tab...
28499         this.fireEvent('activatepane', pane);
28500         pane.el.addClass('active');
28501         pane.fireEvent('activate');
28502         
28503         
28504     },
28505     
28506     getActivePane : function()
28507     {
28508         var r = false;
28509         Roo.each(this.panes, function(p) {
28510             if(p.el.hasClass('active')){
28511                 r = p;
28512                 return false;
28513             }
28514             
28515             return;
28516         });
28517         
28518         return r;
28519     }
28520     
28521     
28522 });
28523
28524  
28525 /*
28526  * - LGPL
28527  *
28528  * Tab pane
28529  * 
28530  */
28531 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28532 /**
28533  * @class Roo.bootstrap.TabPane
28534  * @extends Roo.bootstrap.Component
28535  * Bootstrap TabPane class
28536  * @cfg {Boolean} active (false | true) Default false
28537  * @cfg {String} title title of panel
28538
28539  * 
28540  * @constructor
28541  * Create a new TabPane
28542  * @param {Object} config The config object
28543  */
28544
28545 Roo.bootstrap.dash.TabPane = function(config){
28546     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28547     
28548     this.addEvents({
28549         // raw events
28550         /**
28551          * @event activate
28552          * When a pane is activated
28553          * @param {Roo.bootstrap.dash.TabPane} pane
28554          */
28555         "activate" : true
28556          
28557     });
28558 };
28559
28560 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28561     
28562     active : false,
28563     title : '',
28564     
28565     // the tabBox that this is attached to.
28566     tab : false,
28567      
28568     getAutoCreate : function() 
28569     {
28570         var cfg = {
28571             tag: 'div',
28572             cls: 'tab-pane'
28573         };
28574         
28575         if(this.active){
28576             cfg.cls += ' active';
28577         }
28578         
28579         return cfg;
28580     },
28581     initEvents  : function()
28582     {
28583         //Roo.log('trigger add pane handler');
28584         this.parent().fireEvent('addpane', this)
28585     },
28586     
28587      /**
28588      * Updates the tab title 
28589      * @param {String} html to set the title to.
28590      */
28591     setTitle: function(str)
28592     {
28593         if (!this.tab) {
28594             return;
28595         }
28596         this.title = str;
28597         this.tab.select('a', true).first().dom.innerHTML = str;
28598         
28599     }
28600     
28601     
28602     
28603 });
28604
28605  
28606
28607
28608  /*
28609  * - LGPL
28610  *
28611  * menu
28612  * 
28613  */
28614 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28615
28616 /**
28617  * @class Roo.bootstrap.menu.Menu
28618  * @extends Roo.bootstrap.Component
28619  * Bootstrap Menu class - container for Menu
28620  * @cfg {String} html Text of the menu
28621  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28622  * @cfg {String} icon Font awesome icon
28623  * @cfg {String} pos Menu align to (top | bottom) default bottom
28624  * 
28625  * 
28626  * @constructor
28627  * Create a new Menu
28628  * @param {Object} config The config object
28629  */
28630
28631
28632 Roo.bootstrap.menu.Menu = function(config){
28633     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28634     
28635     this.addEvents({
28636         /**
28637          * @event beforeshow
28638          * Fires before this menu is displayed
28639          * @param {Roo.bootstrap.menu.Menu} this
28640          */
28641         beforeshow : true,
28642         /**
28643          * @event beforehide
28644          * Fires before this menu is hidden
28645          * @param {Roo.bootstrap.menu.Menu} this
28646          */
28647         beforehide : true,
28648         /**
28649          * @event show
28650          * Fires after this menu is displayed
28651          * @param {Roo.bootstrap.menu.Menu} this
28652          */
28653         show : true,
28654         /**
28655          * @event hide
28656          * Fires after this menu is hidden
28657          * @param {Roo.bootstrap.menu.Menu} this
28658          */
28659         hide : true,
28660         /**
28661          * @event click
28662          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28663          * @param {Roo.bootstrap.menu.Menu} this
28664          * @param {Roo.EventObject} e
28665          */
28666         click : true
28667     });
28668     
28669 };
28670
28671 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28672     
28673     submenu : false,
28674     html : '',
28675     weight : 'default',
28676     icon : false,
28677     pos : 'bottom',
28678     
28679     
28680     getChildContainer : function() {
28681         if(this.isSubMenu){
28682             return this.el;
28683         }
28684         
28685         return this.el.select('ul.dropdown-menu', true).first();  
28686     },
28687     
28688     getAutoCreate : function()
28689     {
28690         var text = [
28691             {
28692                 tag : 'span',
28693                 cls : 'roo-menu-text',
28694                 html : this.html
28695             }
28696         ];
28697         
28698         if(this.icon){
28699             text.unshift({
28700                 tag : 'i',
28701                 cls : 'fa ' + this.icon
28702             })
28703         }
28704         
28705         
28706         var cfg = {
28707             tag : 'div',
28708             cls : 'btn-group',
28709             cn : [
28710                 {
28711                     tag : 'button',
28712                     cls : 'dropdown-button btn btn-' + this.weight,
28713                     cn : text
28714                 },
28715                 {
28716                     tag : 'button',
28717                     cls : 'dropdown-toggle btn btn-' + this.weight,
28718                     cn : [
28719                         {
28720                             tag : 'span',
28721                             cls : 'caret'
28722                         }
28723                     ]
28724                 },
28725                 {
28726                     tag : 'ul',
28727                     cls : 'dropdown-menu'
28728                 }
28729             ]
28730             
28731         };
28732         
28733         if(this.pos == 'top'){
28734             cfg.cls += ' dropup';
28735         }
28736         
28737         if(this.isSubMenu){
28738             cfg = {
28739                 tag : 'ul',
28740                 cls : 'dropdown-menu'
28741             }
28742         }
28743         
28744         return cfg;
28745     },
28746     
28747     onRender : function(ct, position)
28748     {
28749         this.isSubMenu = ct.hasClass('dropdown-submenu');
28750         
28751         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28752     },
28753     
28754     initEvents : function() 
28755     {
28756         if(this.isSubMenu){
28757             return;
28758         }
28759         
28760         this.hidden = true;
28761         
28762         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28763         this.triggerEl.on('click', this.onTriggerPress, this);
28764         
28765         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28766         this.buttonEl.on('click', this.onClick, this);
28767         
28768     },
28769     
28770     list : function()
28771     {
28772         if(this.isSubMenu){
28773             return this.el;
28774         }
28775         
28776         return this.el.select('ul.dropdown-menu', true).first();
28777     },
28778     
28779     onClick : function(e)
28780     {
28781         this.fireEvent("click", this, e);
28782     },
28783     
28784     onTriggerPress  : function(e)
28785     {   
28786         if (this.isVisible()) {
28787             this.hide();
28788         } else {
28789             this.show();
28790         }
28791     },
28792     
28793     isVisible : function(){
28794         return !this.hidden;
28795     },
28796     
28797     show : function()
28798     {
28799         this.fireEvent("beforeshow", this);
28800         
28801         this.hidden = false;
28802         this.el.addClass('open');
28803         
28804         Roo.get(document).on("mouseup", this.onMouseUp, this);
28805         
28806         this.fireEvent("show", this);
28807         
28808         
28809     },
28810     
28811     hide : function()
28812     {
28813         this.fireEvent("beforehide", this);
28814         
28815         this.hidden = true;
28816         this.el.removeClass('open');
28817         
28818         Roo.get(document).un("mouseup", this.onMouseUp);
28819         
28820         this.fireEvent("hide", this);
28821     },
28822     
28823     onMouseUp : function()
28824     {
28825         this.hide();
28826     }
28827     
28828 });
28829
28830  
28831  /*
28832  * - LGPL
28833  *
28834  * menu item
28835  * 
28836  */
28837 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28838
28839 /**
28840  * @class Roo.bootstrap.menu.Item
28841  * @extends Roo.bootstrap.Component
28842  * Bootstrap MenuItem class
28843  * @cfg {Boolean} submenu (true | false) default false
28844  * @cfg {String} html text of the item
28845  * @cfg {String} href the link
28846  * @cfg {Boolean} disable (true | false) default false
28847  * @cfg {Boolean} preventDefault (true | false) default true
28848  * @cfg {String} icon Font awesome icon
28849  * @cfg {String} pos Submenu align to (left | right) default right 
28850  * 
28851  * 
28852  * @constructor
28853  * Create a new Item
28854  * @param {Object} config The config object
28855  */
28856
28857
28858 Roo.bootstrap.menu.Item = function(config){
28859     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28860     this.addEvents({
28861         /**
28862          * @event mouseover
28863          * Fires when the mouse is hovering over this menu
28864          * @param {Roo.bootstrap.menu.Item} this
28865          * @param {Roo.EventObject} e
28866          */
28867         mouseover : true,
28868         /**
28869          * @event mouseout
28870          * Fires when the mouse exits this menu
28871          * @param {Roo.bootstrap.menu.Item} this
28872          * @param {Roo.EventObject} e
28873          */
28874         mouseout : true,
28875         // raw events
28876         /**
28877          * @event click
28878          * The raw click event for the entire grid.
28879          * @param {Roo.EventObject} e
28880          */
28881         click : true
28882     });
28883 };
28884
28885 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28886     
28887     submenu : false,
28888     href : '',
28889     html : '',
28890     preventDefault: true,
28891     disable : false,
28892     icon : false,
28893     pos : 'right',
28894     
28895     getAutoCreate : function()
28896     {
28897         var text = [
28898             {
28899                 tag : 'span',
28900                 cls : 'roo-menu-item-text',
28901                 html : this.html
28902             }
28903         ];
28904         
28905         if(this.icon){
28906             text.unshift({
28907                 tag : 'i',
28908                 cls : 'fa ' + this.icon
28909             })
28910         }
28911         
28912         var cfg = {
28913             tag : 'li',
28914             cn : [
28915                 {
28916                     tag : 'a',
28917                     href : this.href || '#',
28918                     cn : text
28919                 }
28920             ]
28921         };
28922         
28923         if(this.disable){
28924             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28925         }
28926         
28927         if(this.submenu){
28928             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28929             
28930             if(this.pos == 'left'){
28931                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28932             }
28933         }
28934         
28935         return cfg;
28936     },
28937     
28938     initEvents : function() 
28939     {
28940         this.el.on('mouseover', this.onMouseOver, this);
28941         this.el.on('mouseout', this.onMouseOut, this);
28942         
28943         this.el.select('a', true).first().on('click', this.onClick, this);
28944         
28945     },
28946     
28947     onClick : function(e)
28948     {
28949         if(this.preventDefault){
28950             e.preventDefault();
28951         }
28952         
28953         this.fireEvent("click", this, e);
28954     },
28955     
28956     onMouseOver : function(e)
28957     {
28958         if(this.submenu && this.pos == 'left'){
28959             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28960         }
28961         
28962         this.fireEvent("mouseover", this, e);
28963     },
28964     
28965     onMouseOut : function(e)
28966     {
28967         this.fireEvent("mouseout", this, e);
28968     }
28969 });
28970
28971  
28972
28973  /*
28974  * - LGPL
28975  *
28976  * menu separator
28977  * 
28978  */
28979 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28980
28981 /**
28982  * @class Roo.bootstrap.menu.Separator
28983  * @extends Roo.bootstrap.Component
28984  * Bootstrap Separator class
28985  * 
28986  * @constructor
28987  * Create a new Separator
28988  * @param {Object} config The config object
28989  */
28990
28991
28992 Roo.bootstrap.menu.Separator = function(config){
28993     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28994 };
28995
28996 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28997     
28998     getAutoCreate : function(){
28999         var cfg = {
29000             tag : 'li',
29001             cls: 'dropdown-divider divider'
29002         };
29003         
29004         return cfg;
29005     }
29006    
29007 });
29008
29009  
29010
29011  /*
29012  * - LGPL
29013  *
29014  * Tooltip
29015  * 
29016  */
29017
29018 /**
29019  * @class Roo.bootstrap.Tooltip
29020  * Bootstrap Tooltip class
29021  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29022  * to determine which dom element triggers the tooltip.
29023  * 
29024  * It needs to add support for additional attributes like tooltip-position
29025  * 
29026  * @constructor
29027  * Create a new Toolti
29028  * @param {Object} config The config object
29029  */
29030
29031 Roo.bootstrap.Tooltip = function(config){
29032     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29033     
29034     this.alignment = Roo.bootstrap.Tooltip.alignment;
29035     
29036     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29037         this.alignment = config.alignment;
29038     }
29039     
29040 };
29041
29042 Roo.apply(Roo.bootstrap.Tooltip, {
29043     /**
29044      * @function init initialize tooltip monitoring.
29045      * @static
29046      */
29047     currentEl : false,
29048     currentTip : false,
29049     currentRegion : false,
29050     
29051     //  init : delay?
29052     
29053     init : function()
29054     {
29055         Roo.get(document).on('mouseover', this.enter ,this);
29056         Roo.get(document).on('mouseout', this.leave, this);
29057          
29058         
29059         this.currentTip = new Roo.bootstrap.Tooltip();
29060     },
29061     
29062     enter : function(ev)
29063     {
29064         var dom = ev.getTarget();
29065         
29066         //Roo.log(['enter',dom]);
29067         var el = Roo.fly(dom);
29068         if (this.currentEl) {
29069             //Roo.log(dom);
29070             //Roo.log(this.currentEl);
29071             //Roo.log(this.currentEl.contains(dom));
29072             if (this.currentEl == el) {
29073                 return;
29074             }
29075             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29076                 return;
29077             }
29078
29079         }
29080         
29081         if (this.currentTip.el) {
29082             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29083         }    
29084         //Roo.log(ev);
29085         
29086         if(!el || el.dom == document){
29087             return;
29088         }
29089         
29090         var bindEl = el; 
29091         var pel = false;
29092         if (!el.attr('tooltip')) {
29093             pel = el.findParent("[tooltip]");
29094             if (pel) {
29095                 bindEl = Roo.get(pel);
29096             }
29097         }
29098         
29099        
29100         
29101         // you can not look for children, as if el is the body.. then everythign is the child..
29102         if (!pel && !el.attr('tooltip')) { //
29103             if (!el.select("[tooltip]").elements.length) {
29104                 return;
29105             }
29106             // is the mouse over this child...?
29107             bindEl = el.select("[tooltip]").first();
29108             var xy = ev.getXY();
29109             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29110                 //Roo.log("not in region.");
29111                 return;
29112             }
29113             //Roo.log("child element over..");
29114             
29115         }
29116         this.currentEl = el;
29117         this.currentTip.bind(bindEl);
29118         this.currentRegion = Roo.lib.Region.getRegion(dom);
29119         this.currentTip.enter();
29120         
29121     },
29122     leave : function(ev)
29123     {
29124         var dom = ev.getTarget();
29125         //Roo.log(['leave',dom]);
29126         if (!this.currentEl) {
29127             return;
29128         }
29129         
29130         
29131         if (dom != this.currentEl.dom) {
29132             return;
29133         }
29134         var xy = ev.getXY();
29135         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29136             return;
29137         }
29138         // only activate leave if mouse cursor is outside... bounding box..
29139         
29140         
29141         
29142         
29143         if (this.currentTip) {
29144             this.currentTip.leave();
29145         }
29146         //Roo.log('clear currentEl');
29147         this.currentEl = false;
29148         
29149         
29150     },
29151     alignment : {
29152         'left' : ['r-l', [-2,0], 'right'],
29153         'right' : ['l-r', [2,0], 'left'],
29154         'bottom' : ['t-b', [0,2], 'top'],
29155         'top' : [ 'b-t', [0,-2], 'bottom']
29156     }
29157     
29158 });
29159
29160
29161 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29162     
29163     
29164     bindEl : false,
29165     
29166     delay : null, // can be { show : 300 , hide: 500}
29167     
29168     timeout : null,
29169     
29170     hoverState : null, //???
29171     
29172     placement : 'bottom', 
29173     
29174     alignment : false,
29175     
29176     getAutoCreate : function(){
29177     
29178         var cfg = {
29179            cls : 'tooltip',   
29180            role : 'tooltip',
29181            cn : [
29182                 {
29183                     cls : 'tooltip-arrow arrow'
29184                 },
29185                 {
29186                     cls : 'tooltip-inner'
29187                 }
29188            ]
29189         };
29190         
29191         return cfg;
29192     },
29193     bind : function(el)
29194     {
29195         this.bindEl = el;
29196     },
29197     
29198     initEvents : function()
29199     {
29200         this.arrowEl = this.el.select('.arrow', true).first();
29201         this.innerEl = this.el.select('.tooltip-inner', true).first();
29202     },
29203     
29204     enter : function () {
29205        
29206         if (this.timeout != null) {
29207             clearTimeout(this.timeout);
29208         }
29209         
29210         this.hoverState = 'in';
29211          //Roo.log("enter - show");
29212         if (!this.delay || !this.delay.show) {
29213             this.show();
29214             return;
29215         }
29216         var _t = this;
29217         this.timeout = setTimeout(function () {
29218             if (_t.hoverState == 'in') {
29219                 _t.show();
29220             }
29221         }, this.delay.show);
29222     },
29223     leave : function()
29224     {
29225         clearTimeout(this.timeout);
29226     
29227         this.hoverState = 'out';
29228          if (!this.delay || !this.delay.hide) {
29229             this.hide();
29230             return;
29231         }
29232        
29233         var _t = this;
29234         this.timeout = setTimeout(function () {
29235             //Roo.log("leave - timeout");
29236             
29237             if (_t.hoverState == 'out') {
29238                 _t.hide();
29239                 Roo.bootstrap.Tooltip.currentEl = false;
29240             }
29241         }, delay);
29242     },
29243     
29244     show : function (msg)
29245     {
29246         if (!this.el) {
29247             this.render(document.body);
29248         }
29249         // set content.
29250         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29251         
29252         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29253         
29254         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29255         
29256         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29257                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29258         
29259         var placement = typeof this.placement == 'function' ?
29260             this.placement.call(this, this.el, on_el) :
29261             this.placement;
29262             
29263         var autoToken = /\s?auto?\s?/i;
29264         var autoPlace = autoToken.test(placement);
29265         if (autoPlace) {
29266             placement = placement.replace(autoToken, '') || 'top';
29267         }
29268         
29269         //this.el.detach()
29270         //this.el.setXY([0,0]);
29271         this.el.show();
29272         //this.el.dom.style.display='block';
29273         
29274         //this.el.appendTo(on_el);
29275         
29276         var p = this.getPosition();
29277         var box = this.el.getBox();
29278         
29279         if (autoPlace) {
29280             // fixme..
29281         }
29282         
29283         var align = this.alignment[placement];
29284         
29285         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29286         
29287         if(placement == 'top' || placement == 'bottom'){
29288             if(xy[0] < 0){
29289                 placement = 'right';
29290             }
29291             
29292             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29293                 placement = 'left';
29294             }
29295             
29296             var scroll = Roo.select('body', true).first().getScroll();
29297             
29298             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29299                 placement = 'top';
29300             }
29301             
29302             align = this.alignment[placement];
29303             
29304             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29305             
29306         }
29307         
29308         var elems = document.getElementsByTagName('div');
29309         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29310         for (var i = 0; i < elems.length; i++) {
29311           var zindex = Number.parseInt(
29312                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29313                 10
29314           );
29315           if (zindex > highest) {
29316             highest = zindex;
29317           }
29318         }
29319         
29320         
29321         
29322         this.el.dom.style.zIndex = highest;
29323         
29324         this.el.alignTo(this.bindEl, align[0],align[1]);
29325         //var arrow = this.el.select('.arrow',true).first();
29326         //arrow.set(align[2], 
29327         
29328         this.el.addClass(placement);
29329         this.el.addClass("bs-tooltip-"+ placement);
29330         
29331         this.el.addClass('in fade show');
29332         
29333         this.hoverState = null;
29334         
29335         if (this.el.hasClass('fade')) {
29336             // fade it?
29337         }
29338         
29339         
29340         
29341         
29342         
29343     },
29344     hide : function()
29345     {
29346          
29347         if (!this.el) {
29348             return;
29349         }
29350         //this.el.setXY([0,0]);
29351         this.el.removeClass(['show', 'in']);
29352         //this.el.hide();
29353         
29354     }
29355     
29356 });
29357  
29358
29359  /*
29360  * - LGPL
29361  *
29362  * Location Picker
29363  * 
29364  */
29365
29366 /**
29367  * @class Roo.bootstrap.LocationPicker
29368  * @extends Roo.bootstrap.Component
29369  * Bootstrap LocationPicker class
29370  * @cfg {Number} latitude Position when init default 0
29371  * @cfg {Number} longitude Position when init default 0
29372  * @cfg {Number} zoom default 15
29373  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29374  * @cfg {Boolean} mapTypeControl default false
29375  * @cfg {Boolean} disableDoubleClickZoom default false
29376  * @cfg {Boolean} scrollwheel default true
29377  * @cfg {Boolean} streetViewControl default false
29378  * @cfg {Number} radius default 0
29379  * @cfg {String} locationName
29380  * @cfg {Boolean} draggable default true
29381  * @cfg {Boolean} enableAutocomplete default false
29382  * @cfg {Boolean} enableReverseGeocode default true
29383  * @cfg {String} markerTitle
29384  * 
29385  * @constructor
29386  * Create a new LocationPicker
29387  * @param {Object} config The config object
29388  */
29389
29390
29391 Roo.bootstrap.LocationPicker = function(config){
29392     
29393     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29394     
29395     this.addEvents({
29396         /**
29397          * @event initial
29398          * Fires when the picker initialized.
29399          * @param {Roo.bootstrap.LocationPicker} this
29400          * @param {Google Location} location
29401          */
29402         initial : true,
29403         /**
29404          * @event positionchanged
29405          * Fires when the picker position changed.
29406          * @param {Roo.bootstrap.LocationPicker} this
29407          * @param {Google Location} location
29408          */
29409         positionchanged : true,
29410         /**
29411          * @event resize
29412          * Fires when the map resize.
29413          * @param {Roo.bootstrap.LocationPicker} this
29414          */
29415         resize : true,
29416         /**
29417          * @event show
29418          * Fires when the map show.
29419          * @param {Roo.bootstrap.LocationPicker} this
29420          */
29421         show : true,
29422         /**
29423          * @event hide
29424          * Fires when the map hide.
29425          * @param {Roo.bootstrap.LocationPicker} this
29426          */
29427         hide : true,
29428         /**
29429          * @event mapClick
29430          * Fires when click the map.
29431          * @param {Roo.bootstrap.LocationPicker} this
29432          * @param {Map event} e
29433          */
29434         mapClick : true,
29435         /**
29436          * @event mapRightClick
29437          * Fires when right click the map.
29438          * @param {Roo.bootstrap.LocationPicker} this
29439          * @param {Map event} e
29440          */
29441         mapRightClick : true,
29442         /**
29443          * @event markerClick
29444          * Fires when click the marker.
29445          * @param {Roo.bootstrap.LocationPicker} this
29446          * @param {Map event} e
29447          */
29448         markerClick : true,
29449         /**
29450          * @event markerRightClick
29451          * Fires when right click the marker.
29452          * @param {Roo.bootstrap.LocationPicker} this
29453          * @param {Map event} e
29454          */
29455         markerRightClick : true,
29456         /**
29457          * @event OverlayViewDraw
29458          * Fires when OverlayView Draw
29459          * @param {Roo.bootstrap.LocationPicker} this
29460          */
29461         OverlayViewDraw : true,
29462         /**
29463          * @event OverlayViewOnAdd
29464          * Fires when OverlayView Draw
29465          * @param {Roo.bootstrap.LocationPicker} this
29466          */
29467         OverlayViewOnAdd : true,
29468         /**
29469          * @event OverlayViewOnRemove
29470          * Fires when OverlayView Draw
29471          * @param {Roo.bootstrap.LocationPicker} this
29472          */
29473         OverlayViewOnRemove : true,
29474         /**
29475          * @event OverlayViewShow
29476          * Fires when OverlayView Draw
29477          * @param {Roo.bootstrap.LocationPicker} this
29478          * @param {Pixel} cpx
29479          */
29480         OverlayViewShow : true,
29481         /**
29482          * @event OverlayViewHide
29483          * Fires when OverlayView Draw
29484          * @param {Roo.bootstrap.LocationPicker} this
29485          */
29486         OverlayViewHide : true,
29487         /**
29488          * @event loadexception
29489          * Fires when load google lib failed.
29490          * @param {Roo.bootstrap.LocationPicker} this
29491          */
29492         loadexception : true
29493     });
29494         
29495 };
29496
29497 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29498     
29499     gMapContext: false,
29500     
29501     latitude: 0,
29502     longitude: 0,
29503     zoom: 15,
29504     mapTypeId: false,
29505     mapTypeControl: false,
29506     disableDoubleClickZoom: false,
29507     scrollwheel: true,
29508     streetViewControl: false,
29509     radius: 0,
29510     locationName: '',
29511     draggable: true,
29512     enableAutocomplete: false,
29513     enableReverseGeocode: true,
29514     markerTitle: '',
29515     
29516     getAutoCreate: function()
29517     {
29518
29519         var cfg = {
29520             tag: 'div',
29521             cls: 'roo-location-picker'
29522         };
29523         
29524         return cfg
29525     },
29526     
29527     initEvents: function(ct, position)
29528     {       
29529         if(!this.el.getWidth() || this.isApplied()){
29530             return;
29531         }
29532         
29533         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29534         
29535         this.initial();
29536     },
29537     
29538     initial: function()
29539     {
29540         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29541             this.fireEvent('loadexception', this);
29542             return;
29543         }
29544         
29545         if(!this.mapTypeId){
29546             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29547         }
29548         
29549         this.gMapContext = this.GMapContext();
29550         
29551         this.initOverlayView();
29552         
29553         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29554         
29555         var _this = this;
29556                 
29557         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29558             _this.setPosition(_this.gMapContext.marker.position);
29559         });
29560         
29561         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29562             _this.fireEvent('mapClick', this, event);
29563             
29564         });
29565
29566         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29567             _this.fireEvent('mapRightClick', this, event);
29568             
29569         });
29570         
29571         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29572             _this.fireEvent('markerClick', this, event);
29573             
29574         });
29575
29576         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29577             _this.fireEvent('markerRightClick', this, event);
29578             
29579         });
29580         
29581         this.setPosition(this.gMapContext.location);
29582         
29583         this.fireEvent('initial', this, this.gMapContext.location);
29584     },
29585     
29586     initOverlayView: function()
29587     {
29588         var _this = this;
29589         
29590         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29591             
29592             draw: function()
29593             {
29594                 _this.fireEvent('OverlayViewDraw', _this);
29595             },
29596             
29597             onAdd: function()
29598             {
29599                 _this.fireEvent('OverlayViewOnAdd', _this);
29600             },
29601             
29602             onRemove: function()
29603             {
29604                 _this.fireEvent('OverlayViewOnRemove', _this);
29605             },
29606             
29607             show: function(cpx)
29608             {
29609                 _this.fireEvent('OverlayViewShow', _this, cpx);
29610             },
29611             
29612             hide: function()
29613             {
29614                 _this.fireEvent('OverlayViewHide', _this);
29615             }
29616             
29617         });
29618     },
29619     
29620     fromLatLngToContainerPixel: function(event)
29621     {
29622         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29623     },
29624     
29625     isApplied: function() 
29626     {
29627         return this.getGmapContext() == false ? false : true;
29628     },
29629     
29630     getGmapContext: function() 
29631     {
29632         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29633     },
29634     
29635     GMapContext: function() 
29636     {
29637         var position = new google.maps.LatLng(this.latitude, this.longitude);
29638         
29639         var _map = new google.maps.Map(this.el.dom, {
29640             center: position,
29641             zoom: this.zoom,
29642             mapTypeId: this.mapTypeId,
29643             mapTypeControl: this.mapTypeControl,
29644             disableDoubleClickZoom: this.disableDoubleClickZoom,
29645             scrollwheel: this.scrollwheel,
29646             streetViewControl: this.streetViewControl,
29647             locationName: this.locationName,
29648             draggable: this.draggable,
29649             enableAutocomplete: this.enableAutocomplete,
29650             enableReverseGeocode: this.enableReverseGeocode
29651         });
29652         
29653         var _marker = new google.maps.Marker({
29654             position: position,
29655             map: _map,
29656             title: this.markerTitle,
29657             draggable: this.draggable
29658         });
29659         
29660         return {
29661             map: _map,
29662             marker: _marker,
29663             circle: null,
29664             location: position,
29665             radius: this.radius,
29666             locationName: this.locationName,
29667             addressComponents: {
29668                 formatted_address: null,
29669                 addressLine1: null,
29670                 addressLine2: null,
29671                 streetName: null,
29672                 streetNumber: null,
29673                 city: null,
29674                 district: null,
29675                 state: null,
29676                 stateOrProvince: null
29677             },
29678             settings: this,
29679             domContainer: this.el.dom,
29680             geodecoder: new google.maps.Geocoder()
29681         };
29682     },
29683     
29684     drawCircle: function(center, radius, options) 
29685     {
29686         if (this.gMapContext.circle != null) {
29687             this.gMapContext.circle.setMap(null);
29688         }
29689         if (radius > 0) {
29690             radius *= 1;
29691             options = Roo.apply({}, options, {
29692                 strokeColor: "#0000FF",
29693                 strokeOpacity: .35,
29694                 strokeWeight: 2,
29695                 fillColor: "#0000FF",
29696                 fillOpacity: .2
29697             });
29698             
29699             options.map = this.gMapContext.map;
29700             options.radius = radius;
29701             options.center = center;
29702             this.gMapContext.circle = new google.maps.Circle(options);
29703             return this.gMapContext.circle;
29704         }
29705         
29706         return null;
29707     },
29708     
29709     setPosition: function(location) 
29710     {
29711         this.gMapContext.location = location;
29712         this.gMapContext.marker.setPosition(location);
29713         this.gMapContext.map.panTo(location);
29714         this.drawCircle(location, this.gMapContext.radius, {});
29715         
29716         var _this = this;
29717         
29718         if (this.gMapContext.settings.enableReverseGeocode) {
29719             this.gMapContext.geodecoder.geocode({
29720                 latLng: this.gMapContext.location
29721             }, function(results, status) {
29722                 
29723                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29724                     _this.gMapContext.locationName = results[0].formatted_address;
29725                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29726                     
29727                     _this.fireEvent('positionchanged', this, location);
29728                 }
29729             });
29730             
29731             return;
29732         }
29733         
29734         this.fireEvent('positionchanged', this, location);
29735     },
29736     
29737     resize: function()
29738     {
29739         google.maps.event.trigger(this.gMapContext.map, "resize");
29740         
29741         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29742         
29743         this.fireEvent('resize', this);
29744     },
29745     
29746     setPositionByLatLng: function(latitude, longitude)
29747     {
29748         this.setPosition(new google.maps.LatLng(latitude, longitude));
29749     },
29750     
29751     getCurrentPosition: function() 
29752     {
29753         return {
29754             latitude: this.gMapContext.location.lat(),
29755             longitude: this.gMapContext.location.lng()
29756         };
29757     },
29758     
29759     getAddressName: function() 
29760     {
29761         return this.gMapContext.locationName;
29762     },
29763     
29764     getAddressComponents: function() 
29765     {
29766         return this.gMapContext.addressComponents;
29767     },
29768     
29769     address_component_from_google_geocode: function(address_components) 
29770     {
29771         var result = {};
29772         
29773         for (var i = 0; i < address_components.length; i++) {
29774             var component = address_components[i];
29775             if (component.types.indexOf("postal_code") >= 0) {
29776                 result.postalCode = component.short_name;
29777             } else if (component.types.indexOf("street_number") >= 0) {
29778                 result.streetNumber = component.short_name;
29779             } else if (component.types.indexOf("route") >= 0) {
29780                 result.streetName = component.short_name;
29781             } else if (component.types.indexOf("neighborhood") >= 0) {
29782                 result.city = component.short_name;
29783             } else if (component.types.indexOf("locality") >= 0) {
29784                 result.city = component.short_name;
29785             } else if (component.types.indexOf("sublocality") >= 0) {
29786                 result.district = component.short_name;
29787             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29788                 result.stateOrProvince = component.short_name;
29789             } else if (component.types.indexOf("country") >= 0) {
29790                 result.country = component.short_name;
29791             }
29792         }
29793         
29794         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29795         result.addressLine2 = "";
29796         return result;
29797     },
29798     
29799     setZoomLevel: function(zoom)
29800     {
29801         this.gMapContext.map.setZoom(zoom);
29802     },
29803     
29804     show: function()
29805     {
29806         if(!this.el){
29807             return;
29808         }
29809         
29810         this.el.show();
29811         
29812         this.resize();
29813         
29814         this.fireEvent('show', this);
29815     },
29816     
29817     hide: function()
29818     {
29819         if(!this.el){
29820             return;
29821         }
29822         
29823         this.el.hide();
29824         
29825         this.fireEvent('hide', this);
29826     }
29827     
29828 });
29829
29830 Roo.apply(Roo.bootstrap.LocationPicker, {
29831     
29832     OverlayView : function(map, options)
29833     {
29834         options = options || {};
29835         
29836         this.setMap(map);
29837     }
29838     
29839     
29840 });/**
29841  * @class Roo.bootstrap.Alert
29842  * @extends Roo.bootstrap.Component
29843  * Bootstrap Alert class - shows an alert area box
29844  * eg
29845  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29846   Enter a valid email address
29847 </div>
29848  * @licence LGPL
29849  * @cfg {String} title The title of alert
29850  * @cfg {String} html The content of alert
29851  * @cfg {String} weight (  success | info | warning | danger )
29852  * @cfg {String} fa font-awesomeicon
29853  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29854  * @cfg {Boolean} close true to show a x closer
29855  * 
29856  * 
29857  * @constructor
29858  * Create a new alert
29859  * @param {Object} config The config object
29860  */
29861
29862
29863 Roo.bootstrap.Alert = function(config){
29864     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29865     
29866 };
29867
29868 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29869     
29870     title: '',
29871     html: '',
29872     weight: false,
29873     fa: false,
29874     faicon: false, // BC
29875     close : false,
29876     
29877     
29878     getAutoCreate : function()
29879     {
29880         
29881         var cfg = {
29882             tag : 'div',
29883             cls : 'alert',
29884             cn : [
29885                 {
29886                     tag: 'button',
29887                     type :  "button",
29888                     cls: "close",
29889                     html : '×',
29890                     style : this.close ? '' : 'display:none'
29891                 },
29892                 {
29893                     tag : 'i',
29894                     cls : 'roo-alert-icon'
29895                     
29896                 },
29897                 {
29898                     tag : 'b',
29899                     cls : 'roo-alert-title',
29900                     html : this.title
29901                 },
29902                 {
29903                     tag : 'span',
29904                     cls : 'roo-alert-text',
29905                     html : this.html
29906                 }
29907             ]
29908         };
29909         
29910         if(this.faicon){
29911             cfg.cn[0].cls += ' fa ' + this.faicon;
29912         }
29913         if(this.fa){
29914             cfg.cn[0].cls += ' fa ' + this.fa;
29915         }
29916         
29917         if(this.weight){
29918             cfg.cls += ' alert-' + this.weight;
29919         }
29920         
29921         return cfg;
29922     },
29923     
29924     initEvents: function() 
29925     {
29926         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29927         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29928         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29929         if (this.seconds > 0) {
29930             this.hide.defer(this.seconds, this);
29931         }
29932     },
29933     
29934     setTitle : function(str)
29935     {
29936         this.titleEl.dom.innerHTML = str;
29937     },
29938     
29939     setText : function(str)
29940     {
29941         this.titleEl.dom.innerHTML = str;
29942     },
29943     
29944     setWeight : function(weight)
29945     {
29946         if(this.weight){
29947             this.el.removeClass('alert-' + this.weight);
29948         }
29949         
29950         this.weight = weight;
29951         
29952         this.el.addClass('alert-' + this.weight);
29953     },
29954     
29955     setIcon : function(icon)
29956     {
29957         if(this.faicon){
29958             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29959         }
29960         
29961         this.faicon = icon;
29962         
29963         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29964     },
29965     
29966     hide: function() 
29967     {
29968         this.el.hide();   
29969     },
29970     
29971     show: function() 
29972     {  
29973         this.el.show();   
29974     }
29975     
29976 });
29977
29978  
29979 /*
29980 * Licence: LGPL
29981 */
29982
29983 /**
29984  * @class Roo.bootstrap.UploadCropbox
29985  * @extends Roo.bootstrap.Component
29986  * Bootstrap UploadCropbox class
29987  * @cfg {String} emptyText show when image has been loaded
29988  * @cfg {String} rotateNotify show when image too small to rotate
29989  * @cfg {Number} errorTimeout default 3000
29990  * @cfg {Number} minWidth default 300
29991  * @cfg {Number} minHeight default 300
29992  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29993  * @cfg {Boolean} isDocument (true|false) default false
29994  * @cfg {String} url action url
29995  * @cfg {String} paramName default 'imageUpload'
29996  * @cfg {String} method default POST
29997  * @cfg {Boolean} loadMask (true|false) default true
29998  * @cfg {Boolean} loadingText default 'Loading...'
29999  * 
30000  * @constructor
30001  * Create a new UploadCropbox
30002  * @param {Object} config The config object
30003  */
30004
30005 Roo.bootstrap.UploadCropbox = function(config){
30006     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30007     
30008     this.addEvents({
30009         /**
30010          * @event beforeselectfile
30011          * Fire before select file
30012          * @param {Roo.bootstrap.UploadCropbox} this
30013          */
30014         "beforeselectfile" : true,
30015         /**
30016          * @event initial
30017          * Fire after initEvent
30018          * @param {Roo.bootstrap.UploadCropbox} this
30019          */
30020         "initial" : true,
30021         /**
30022          * @event crop
30023          * Fire after initEvent
30024          * @param {Roo.bootstrap.UploadCropbox} this
30025          * @param {String} data
30026          */
30027         "crop" : true,
30028         /**
30029          * @event prepare
30030          * Fire when preparing the file data
30031          * @param {Roo.bootstrap.UploadCropbox} this
30032          * @param {Object} file
30033          */
30034         "prepare" : true,
30035         /**
30036          * @event exception
30037          * Fire when get exception
30038          * @param {Roo.bootstrap.UploadCropbox} this
30039          * @param {XMLHttpRequest} xhr
30040          */
30041         "exception" : true,
30042         /**
30043          * @event beforeloadcanvas
30044          * Fire before load the canvas
30045          * @param {Roo.bootstrap.UploadCropbox} this
30046          * @param {String} src
30047          */
30048         "beforeloadcanvas" : true,
30049         /**
30050          * @event trash
30051          * Fire when trash image
30052          * @param {Roo.bootstrap.UploadCropbox} this
30053          */
30054         "trash" : true,
30055         /**
30056          * @event download
30057          * Fire when download the image
30058          * @param {Roo.bootstrap.UploadCropbox} this
30059          */
30060         "download" : true,
30061         /**
30062          * @event footerbuttonclick
30063          * Fire when footerbuttonclick
30064          * @param {Roo.bootstrap.UploadCropbox} this
30065          * @param {String} type
30066          */
30067         "footerbuttonclick" : true,
30068         /**
30069          * @event resize
30070          * Fire when resize
30071          * @param {Roo.bootstrap.UploadCropbox} this
30072          */
30073         "resize" : true,
30074         /**
30075          * @event rotate
30076          * Fire when rotate the image
30077          * @param {Roo.bootstrap.UploadCropbox} this
30078          * @param {String} pos
30079          */
30080         "rotate" : true,
30081         /**
30082          * @event inspect
30083          * Fire when inspect the file
30084          * @param {Roo.bootstrap.UploadCropbox} this
30085          * @param {Object} file
30086          */
30087         "inspect" : true,
30088         /**
30089          * @event upload
30090          * Fire when xhr upload the file
30091          * @param {Roo.bootstrap.UploadCropbox} this
30092          * @param {Object} data
30093          */
30094         "upload" : true,
30095         /**
30096          * @event arrange
30097          * Fire when arrange the file data
30098          * @param {Roo.bootstrap.UploadCropbox} this
30099          * @param {Object} formData
30100          */
30101         "arrange" : true
30102     });
30103     
30104     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30105 };
30106
30107 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30108     
30109     emptyText : 'Click to upload image',
30110     rotateNotify : 'Image is too small to rotate',
30111     errorTimeout : 3000,
30112     scale : 0,
30113     baseScale : 1,
30114     rotate : 0,
30115     dragable : false,
30116     pinching : false,
30117     mouseX : 0,
30118     mouseY : 0,
30119     cropData : false,
30120     minWidth : 300,
30121     minHeight : 300,
30122     file : false,
30123     exif : {},
30124     baseRotate : 1,
30125     cropType : 'image/jpeg',
30126     buttons : false,
30127     canvasLoaded : false,
30128     isDocument : false,
30129     method : 'POST',
30130     paramName : 'imageUpload',
30131     loadMask : true,
30132     loadingText : 'Loading...',
30133     maskEl : false,
30134     
30135     getAutoCreate : function()
30136     {
30137         var cfg = {
30138             tag : 'div',
30139             cls : 'roo-upload-cropbox',
30140             cn : [
30141                 {
30142                     tag : 'input',
30143                     cls : 'roo-upload-cropbox-selector',
30144                     type : 'file'
30145                 },
30146                 {
30147                     tag : 'div',
30148                     cls : 'roo-upload-cropbox-body',
30149                     style : 'cursor:pointer',
30150                     cn : [
30151                         {
30152                             tag : 'div',
30153                             cls : 'roo-upload-cropbox-preview'
30154                         },
30155                         {
30156                             tag : 'div',
30157                             cls : 'roo-upload-cropbox-thumb'
30158                         },
30159                         {
30160                             tag : 'div',
30161                             cls : 'roo-upload-cropbox-empty-notify',
30162                             html : this.emptyText
30163                         },
30164                         {
30165                             tag : 'div',
30166                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30167                             html : this.rotateNotify
30168                         }
30169                     ]
30170                 },
30171                 {
30172                     tag : 'div',
30173                     cls : 'roo-upload-cropbox-footer',
30174                     cn : {
30175                         tag : 'div',
30176                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30177                         cn : []
30178                     }
30179                 }
30180             ]
30181         };
30182         
30183         return cfg;
30184     },
30185     
30186     onRender : function(ct, position)
30187     {
30188         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30189         
30190         if (this.buttons.length) {
30191             
30192             Roo.each(this.buttons, function(bb) {
30193                 
30194                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30195                 
30196                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30197                 
30198             }, this);
30199         }
30200         
30201         if(this.loadMask){
30202             this.maskEl = this.el;
30203         }
30204     },
30205     
30206     initEvents : function()
30207     {
30208         this.urlAPI = (window.createObjectURL && window) || 
30209                                 (window.URL && URL.revokeObjectURL && URL) || 
30210                                 (window.webkitURL && webkitURL);
30211                         
30212         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30213         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30214         
30215         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30216         this.selectorEl.hide();
30217         
30218         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30219         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30220         
30221         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30222         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30223         this.thumbEl.hide();
30224         
30225         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30226         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30227         
30228         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30229         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30230         this.errorEl.hide();
30231         
30232         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30233         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30234         this.footerEl.hide();
30235         
30236         this.setThumbBoxSize();
30237         
30238         this.bind();
30239         
30240         this.resize();
30241         
30242         this.fireEvent('initial', this);
30243     },
30244
30245     bind : function()
30246     {
30247         var _this = this;
30248         
30249         window.addEventListener("resize", function() { _this.resize(); } );
30250         
30251         this.bodyEl.on('click', this.beforeSelectFile, this);
30252         
30253         if(Roo.isTouch){
30254             this.bodyEl.on('touchstart', this.onTouchStart, this);
30255             this.bodyEl.on('touchmove', this.onTouchMove, this);
30256             this.bodyEl.on('touchend', this.onTouchEnd, this);
30257         }
30258         
30259         if(!Roo.isTouch){
30260             this.bodyEl.on('mousedown', this.onMouseDown, this);
30261             this.bodyEl.on('mousemove', this.onMouseMove, this);
30262             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30263             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30264             Roo.get(document).on('mouseup', this.onMouseUp, this);
30265         }
30266         
30267         this.selectorEl.on('change', this.onFileSelected, this);
30268     },
30269     
30270     reset : function()
30271     {    
30272         this.scale = 0;
30273         this.baseScale = 1;
30274         this.rotate = 0;
30275         this.baseRotate = 1;
30276         this.dragable = false;
30277         this.pinching = false;
30278         this.mouseX = 0;
30279         this.mouseY = 0;
30280         this.cropData = false;
30281         this.notifyEl.dom.innerHTML = this.emptyText;
30282         
30283         this.selectorEl.dom.value = '';
30284         
30285     },
30286     
30287     resize : function()
30288     {
30289         if(this.fireEvent('resize', this) != false){
30290             this.setThumbBoxPosition();
30291             this.setCanvasPosition();
30292         }
30293     },
30294     
30295     onFooterButtonClick : function(e, el, o, type)
30296     {
30297         switch (type) {
30298             case 'rotate-left' :
30299                 this.onRotateLeft(e);
30300                 break;
30301             case 'rotate-right' :
30302                 this.onRotateRight(e);
30303                 break;
30304             case 'picture' :
30305                 this.beforeSelectFile(e);
30306                 break;
30307             case 'trash' :
30308                 this.trash(e);
30309                 break;
30310             case 'crop' :
30311                 this.crop(e);
30312                 break;
30313             case 'download' :
30314                 this.download(e);
30315                 break;
30316             default :
30317                 break;
30318         }
30319         
30320         this.fireEvent('footerbuttonclick', this, type);
30321     },
30322     
30323     beforeSelectFile : function(e)
30324     {
30325         e.preventDefault();
30326         
30327         if(this.fireEvent('beforeselectfile', this) != false){
30328             this.selectorEl.dom.click();
30329         }
30330     },
30331     
30332     onFileSelected : function(e)
30333     {
30334         e.preventDefault();
30335         
30336         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30337             return;
30338         }
30339         
30340         var file = this.selectorEl.dom.files[0];
30341         
30342         if(this.fireEvent('inspect', this, file) != false){
30343             this.prepare(file);
30344         }
30345         
30346     },
30347     
30348     trash : function(e)
30349     {
30350         this.fireEvent('trash', this);
30351     },
30352     
30353     download : function(e)
30354     {
30355         this.fireEvent('download', this);
30356     },
30357     
30358     loadCanvas : function(src)
30359     {   
30360         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30361             
30362             this.reset();
30363             
30364             this.imageEl = document.createElement('img');
30365             
30366             var _this = this;
30367             
30368             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30369             
30370             this.imageEl.src = src;
30371         }
30372     },
30373     
30374     onLoadCanvas : function()
30375     {   
30376         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30377         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30378         
30379         this.bodyEl.un('click', this.beforeSelectFile, this);
30380         
30381         this.notifyEl.hide();
30382         this.thumbEl.show();
30383         this.footerEl.show();
30384         
30385         this.baseRotateLevel();
30386         
30387         if(this.isDocument){
30388             this.setThumbBoxSize();
30389         }
30390         
30391         this.setThumbBoxPosition();
30392         
30393         this.baseScaleLevel();
30394         
30395         this.draw();
30396         
30397         this.resize();
30398         
30399         this.canvasLoaded = true;
30400         
30401         if(this.loadMask){
30402             this.maskEl.unmask();
30403         }
30404         
30405     },
30406     
30407     setCanvasPosition : function()
30408     {   
30409         if(!this.canvasEl){
30410             return;
30411         }
30412         
30413         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30414         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30415         
30416         this.previewEl.setLeft(pw);
30417         this.previewEl.setTop(ph);
30418         
30419     },
30420     
30421     onMouseDown : function(e)
30422     {   
30423         e.stopEvent();
30424         
30425         this.dragable = true;
30426         this.pinching = false;
30427         
30428         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30429             this.dragable = false;
30430             return;
30431         }
30432         
30433         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30434         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30435         
30436     },
30437     
30438     onMouseMove : function(e)
30439     {   
30440         e.stopEvent();
30441         
30442         if(!this.canvasLoaded){
30443             return;
30444         }
30445         
30446         if (!this.dragable){
30447             return;
30448         }
30449         
30450         var minX = Math.ceil(this.thumbEl.getLeft(true));
30451         var minY = Math.ceil(this.thumbEl.getTop(true));
30452         
30453         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30454         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30455         
30456         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30457         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30458         
30459         x = x - this.mouseX;
30460         y = y - this.mouseY;
30461         
30462         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30463         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30464         
30465         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30466         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30467         
30468         this.previewEl.setLeft(bgX);
30469         this.previewEl.setTop(bgY);
30470         
30471         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30472         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30473     },
30474     
30475     onMouseUp : function(e)
30476     {   
30477         e.stopEvent();
30478         
30479         this.dragable = false;
30480     },
30481     
30482     onMouseWheel : function(e)
30483     {   
30484         e.stopEvent();
30485         
30486         this.startScale = this.scale;
30487         
30488         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30489         
30490         if(!this.zoomable()){
30491             this.scale = this.startScale;
30492             return;
30493         }
30494         
30495         this.draw();
30496         
30497         return;
30498     },
30499     
30500     zoomable : function()
30501     {
30502         var minScale = this.thumbEl.getWidth() / this.minWidth;
30503         
30504         if(this.minWidth < this.minHeight){
30505             minScale = this.thumbEl.getHeight() / this.minHeight;
30506         }
30507         
30508         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30509         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30510         
30511         if(
30512                 this.isDocument &&
30513                 (this.rotate == 0 || this.rotate == 180) && 
30514                 (
30515                     width > this.imageEl.OriginWidth || 
30516                     height > this.imageEl.OriginHeight ||
30517                     (width < this.minWidth && height < this.minHeight)
30518                 )
30519         ){
30520             return false;
30521         }
30522         
30523         if(
30524                 this.isDocument &&
30525                 (this.rotate == 90 || this.rotate == 270) && 
30526                 (
30527                     width > this.imageEl.OriginWidth || 
30528                     height > this.imageEl.OriginHeight ||
30529                     (width < this.minHeight && height < this.minWidth)
30530                 )
30531         ){
30532             return false;
30533         }
30534         
30535         if(
30536                 !this.isDocument &&
30537                 (this.rotate == 0 || this.rotate == 180) && 
30538                 (
30539                     width < this.minWidth || 
30540                     width > this.imageEl.OriginWidth || 
30541                     height < this.minHeight || 
30542                     height > this.imageEl.OriginHeight
30543                 )
30544         ){
30545             return false;
30546         }
30547         
30548         if(
30549                 !this.isDocument &&
30550                 (this.rotate == 90 || this.rotate == 270) && 
30551                 (
30552                     width < this.minHeight || 
30553                     width > this.imageEl.OriginWidth || 
30554                     height < this.minWidth || 
30555                     height > this.imageEl.OriginHeight
30556                 )
30557         ){
30558             return false;
30559         }
30560         
30561         return true;
30562         
30563     },
30564     
30565     onRotateLeft : function(e)
30566     {   
30567         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30568             
30569             var minScale = this.thumbEl.getWidth() / this.minWidth;
30570             
30571             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30572             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30573             
30574             this.startScale = this.scale;
30575             
30576             while (this.getScaleLevel() < minScale){
30577             
30578                 this.scale = this.scale + 1;
30579                 
30580                 if(!this.zoomable()){
30581                     break;
30582                 }
30583                 
30584                 if(
30585                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30586                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30587                 ){
30588                     continue;
30589                 }
30590                 
30591                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30592
30593                 this.draw();
30594                 
30595                 return;
30596             }
30597             
30598             this.scale = this.startScale;
30599             
30600             this.onRotateFail();
30601             
30602             return false;
30603         }
30604         
30605         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30606
30607         if(this.isDocument){
30608             this.setThumbBoxSize();
30609             this.setThumbBoxPosition();
30610             this.setCanvasPosition();
30611         }
30612         
30613         this.draw();
30614         
30615         this.fireEvent('rotate', this, 'left');
30616         
30617     },
30618     
30619     onRotateRight : function(e)
30620     {
30621         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30622             
30623             var minScale = this.thumbEl.getWidth() / this.minWidth;
30624         
30625             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30626             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30627             
30628             this.startScale = this.scale;
30629             
30630             while (this.getScaleLevel() < minScale){
30631             
30632                 this.scale = this.scale + 1;
30633                 
30634                 if(!this.zoomable()){
30635                     break;
30636                 }
30637                 
30638                 if(
30639                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30640                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30641                 ){
30642                     continue;
30643                 }
30644                 
30645                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30646
30647                 this.draw();
30648                 
30649                 return;
30650             }
30651             
30652             this.scale = this.startScale;
30653             
30654             this.onRotateFail();
30655             
30656             return false;
30657         }
30658         
30659         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30660
30661         if(this.isDocument){
30662             this.setThumbBoxSize();
30663             this.setThumbBoxPosition();
30664             this.setCanvasPosition();
30665         }
30666         
30667         this.draw();
30668         
30669         this.fireEvent('rotate', this, 'right');
30670     },
30671     
30672     onRotateFail : function()
30673     {
30674         this.errorEl.show(true);
30675         
30676         var _this = this;
30677         
30678         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30679     },
30680     
30681     draw : function()
30682     {
30683         this.previewEl.dom.innerHTML = '';
30684         
30685         var canvasEl = document.createElement("canvas");
30686         
30687         var contextEl = canvasEl.getContext("2d");
30688         
30689         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30690         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30691         var center = this.imageEl.OriginWidth / 2;
30692         
30693         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30694             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30695             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30696             center = this.imageEl.OriginHeight / 2;
30697         }
30698         
30699         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30700         
30701         contextEl.translate(center, center);
30702         contextEl.rotate(this.rotate * Math.PI / 180);
30703
30704         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30705         
30706         this.canvasEl = document.createElement("canvas");
30707         
30708         this.contextEl = this.canvasEl.getContext("2d");
30709         
30710         switch (this.rotate) {
30711             case 0 :
30712                 
30713                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30714                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30715                 
30716                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30717                 
30718                 break;
30719             case 90 : 
30720                 
30721                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30722                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30723                 
30724                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30725                     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);
30726                     break;
30727                 }
30728                 
30729                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30730                 
30731                 break;
30732             case 180 :
30733                 
30734                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30735                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30736                 
30737                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30738                     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);
30739                     break;
30740                 }
30741                 
30742                 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);
30743                 
30744                 break;
30745             case 270 :
30746                 
30747                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30748                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30749         
30750                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30751                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30752                     break;
30753                 }
30754                 
30755                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30756                 
30757                 break;
30758             default : 
30759                 break;
30760         }
30761         
30762         this.previewEl.appendChild(this.canvasEl);
30763         
30764         this.setCanvasPosition();
30765     },
30766     
30767     crop : function()
30768     {
30769         if(!this.canvasLoaded){
30770             return;
30771         }
30772         
30773         var imageCanvas = document.createElement("canvas");
30774         
30775         var imageContext = imageCanvas.getContext("2d");
30776         
30777         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30778         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30779         
30780         var center = imageCanvas.width / 2;
30781         
30782         imageContext.translate(center, center);
30783         
30784         imageContext.rotate(this.rotate * Math.PI / 180);
30785         
30786         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30787         
30788         var canvas = document.createElement("canvas");
30789         
30790         var context = canvas.getContext("2d");
30791                 
30792         canvas.width = this.minWidth;
30793         canvas.height = this.minHeight;
30794
30795         switch (this.rotate) {
30796             case 0 :
30797                 
30798                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30799                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30800                 
30801                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30802                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30803                 
30804                 var targetWidth = this.minWidth - 2 * x;
30805                 var targetHeight = this.minHeight - 2 * y;
30806                 
30807                 var scale = 1;
30808                 
30809                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30810                     scale = targetWidth / width;
30811                 }
30812                 
30813                 if(x > 0 && y == 0){
30814                     scale = targetHeight / height;
30815                 }
30816                 
30817                 if(x > 0 && y > 0){
30818                     scale = targetWidth / width;
30819                     
30820                     if(width < height){
30821                         scale = targetHeight / height;
30822                     }
30823                 }
30824                 
30825                 context.scale(scale, scale);
30826                 
30827                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30828                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30829
30830                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30831                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30832
30833                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30834                 
30835                 break;
30836             case 90 : 
30837                 
30838                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30839                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30840                 
30841                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30842                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30843                 
30844                 var targetWidth = this.minWidth - 2 * x;
30845                 var targetHeight = this.minHeight - 2 * y;
30846                 
30847                 var scale = 1;
30848                 
30849                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30850                     scale = targetWidth / width;
30851                 }
30852                 
30853                 if(x > 0 && y == 0){
30854                     scale = targetHeight / height;
30855                 }
30856                 
30857                 if(x > 0 && y > 0){
30858                     scale = targetWidth / width;
30859                     
30860                     if(width < height){
30861                         scale = targetHeight / height;
30862                     }
30863                 }
30864                 
30865                 context.scale(scale, scale);
30866                 
30867                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30868                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30869
30870                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30871                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30872                 
30873                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30874                 
30875                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30876                 
30877                 break;
30878             case 180 :
30879                 
30880                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30881                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30882                 
30883                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30884                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30885                 
30886                 var targetWidth = this.minWidth - 2 * x;
30887                 var targetHeight = this.minHeight - 2 * y;
30888                 
30889                 var scale = 1;
30890                 
30891                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30892                     scale = targetWidth / width;
30893                 }
30894                 
30895                 if(x > 0 && y == 0){
30896                     scale = targetHeight / height;
30897                 }
30898                 
30899                 if(x > 0 && y > 0){
30900                     scale = targetWidth / width;
30901                     
30902                     if(width < height){
30903                         scale = targetHeight / height;
30904                     }
30905                 }
30906                 
30907                 context.scale(scale, scale);
30908                 
30909                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30910                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30911
30912                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30913                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30914
30915                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30916                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30917                 
30918                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30919                 
30920                 break;
30921             case 270 :
30922                 
30923                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30924                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30925                 
30926                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30927                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30928                 
30929                 var targetWidth = this.minWidth - 2 * x;
30930                 var targetHeight = this.minHeight - 2 * y;
30931                 
30932                 var scale = 1;
30933                 
30934                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30935                     scale = targetWidth / width;
30936                 }
30937                 
30938                 if(x > 0 && y == 0){
30939                     scale = targetHeight / height;
30940                 }
30941                 
30942                 if(x > 0 && y > 0){
30943                     scale = targetWidth / width;
30944                     
30945                     if(width < height){
30946                         scale = targetHeight / height;
30947                     }
30948                 }
30949                 
30950                 context.scale(scale, scale);
30951                 
30952                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30953                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30954
30955                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30956                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30957                 
30958                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30959                 
30960                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30961                 
30962                 break;
30963             default : 
30964                 break;
30965         }
30966         
30967         this.cropData = canvas.toDataURL(this.cropType);
30968         
30969         if(this.fireEvent('crop', this, this.cropData) !== false){
30970             this.process(this.file, this.cropData);
30971         }
30972         
30973         return;
30974         
30975     },
30976     
30977     setThumbBoxSize : function()
30978     {
30979         var width, height;
30980         
30981         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30982             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30983             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30984             
30985             this.minWidth = width;
30986             this.minHeight = height;
30987             
30988             if(this.rotate == 90 || this.rotate == 270){
30989                 this.minWidth = height;
30990                 this.minHeight = width;
30991             }
30992         }
30993         
30994         height = 300;
30995         width = Math.ceil(this.minWidth * height / this.minHeight);
30996         
30997         if(this.minWidth > this.minHeight){
30998             width = 300;
30999             height = Math.ceil(this.minHeight * width / this.minWidth);
31000         }
31001         
31002         this.thumbEl.setStyle({
31003             width : width + 'px',
31004             height : height + 'px'
31005         });
31006
31007         return;
31008             
31009     },
31010     
31011     setThumbBoxPosition : function()
31012     {
31013         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31014         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31015         
31016         this.thumbEl.setLeft(x);
31017         this.thumbEl.setTop(y);
31018         
31019     },
31020     
31021     baseRotateLevel : function()
31022     {
31023         this.baseRotate = 1;
31024         
31025         if(
31026                 typeof(this.exif) != 'undefined' &&
31027                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31028                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31029         ){
31030             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31031         }
31032         
31033         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31034         
31035     },
31036     
31037     baseScaleLevel : function()
31038     {
31039         var width, height;
31040         
31041         if(this.isDocument){
31042             
31043             if(this.baseRotate == 6 || this.baseRotate == 8){
31044             
31045                 height = this.thumbEl.getHeight();
31046                 this.baseScale = height / this.imageEl.OriginWidth;
31047
31048                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31049                     width = this.thumbEl.getWidth();
31050                     this.baseScale = width / this.imageEl.OriginHeight;
31051                 }
31052
31053                 return;
31054             }
31055
31056             height = this.thumbEl.getHeight();
31057             this.baseScale = height / this.imageEl.OriginHeight;
31058
31059             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31060                 width = this.thumbEl.getWidth();
31061                 this.baseScale = width / this.imageEl.OriginWidth;
31062             }
31063
31064             return;
31065         }
31066         
31067         if(this.baseRotate == 6 || this.baseRotate == 8){
31068             
31069             width = this.thumbEl.getHeight();
31070             this.baseScale = width / this.imageEl.OriginHeight;
31071             
31072             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31073                 height = this.thumbEl.getWidth();
31074                 this.baseScale = height / this.imageEl.OriginHeight;
31075             }
31076             
31077             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31078                 height = this.thumbEl.getWidth();
31079                 this.baseScale = height / this.imageEl.OriginHeight;
31080                 
31081                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31082                     width = this.thumbEl.getHeight();
31083                     this.baseScale = width / this.imageEl.OriginWidth;
31084                 }
31085             }
31086             
31087             return;
31088         }
31089         
31090         width = this.thumbEl.getWidth();
31091         this.baseScale = width / this.imageEl.OriginWidth;
31092         
31093         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31094             height = this.thumbEl.getHeight();
31095             this.baseScale = height / this.imageEl.OriginHeight;
31096         }
31097         
31098         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31099             
31100             height = this.thumbEl.getHeight();
31101             this.baseScale = height / this.imageEl.OriginHeight;
31102             
31103             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31104                 width = this.thumbEl.getWidth();
31105                 this.baseScale = width / this.imageEl.OriginWidth;
31106             }
31107             
31108         }
31109         
31110         return;
31111     },
31112     
31113     getScaleLevel : function()
31114     {
31115         return this.baseScale * Math.pow(1.1, this.scale);
31116     },
31117     
31118     onTouchStart : function(e)
31119     {
31120         if(!this.canvasLoaded){
31121             this.beforeSelectFile(e);
31122             return;
31123         }
31124         
31125         var touches = e.browserEvent.touches;
31126         
31127         if(!touches){
31128             return;
31129         }
31130         
31131         if(touches.length == 1){
31132             this.onMouseDown(e);
31133             return;
31134         }
31135         
31136         if(touches.length != 2){
31137             return;
31138         }
31139         
31140         var coords = [];
31141         
31142         for(var i = 0, finger; finger = touches[i]; i++){
31143             coords.push(finger.pageX, finger.pageY);
31144         }
31145         
31146         var x = Math.pow(coords[0] - coords[2], 2);
31147         var y = Math.pow(coords[1] - coords[3], 2);
31148         
31149         this.startDistance = Math.sqrt(x + y);
31150         
31151         this.startScale = this.scale;
31152         
31153         this.pinching = true;
31154         this.dragable = false;
31155         
31156     },
31157     
31158     onTouchMove : function(e)
31159     {
31160         if(!this.pinching && !this.dragable){
31161             return;
31162         }
31163         
31164         var touches = e.browserEvent.touches;
31165         
31166         if(!touches){
31167             return;
31168         }
31169         
31170         if(this.dragable){
31171             this.onMouseMove(e);
31172             return;
31173         }
31174         
31175         var coords = [];
31176         
31177         for(var i = 0, finger; finger = touches[i]; i++){
31178             coords.push(finger.pageX, finger.pageY);
31179         }
31180         
31181         var x = Math.pow(coords[0] - coords[2], 2);
31182         var y = Math.pow(coords[1] - coords[3], 2);
31183         
31184         this.endDistance = Math.sqrt(x + y);
31185         
31186         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31187         
31188         if(!this.zoomable()){
31189             this.scale = this.startScale;
31190             return;
31191         }
31192         
31193         this.draw();
31194         
31195     },
31196     
31197     onTouchEnd : function(e)
31198     {
31199         this.pinching = false;
31200         this.dragable = false;
31201         
31202     },
31203     
31204     process : function(file, crop)
31205     {
31206         if(this.loadMask){
31207             this.maskEl.mask(this.loadingText);
31208         }
31209         
31210         this.xhr = new XMLHttpRequest();
31211         
31212         file.xhr = this.xhr;
31213
31214         this.xhr.open(this.method, this.url, true);
31215         
31216         var headers = {
31217             "Accept": "application/json",
31218             "Cache-Control": "no-cache",
31219             "X-Requested-With": "XMLHttpRequest"
31220         };
31221         
31222         for (var headerName in headers) {
31223             var headerValue = headers[headerName];
31224             if (headerValue) {
31225                 this.xhr.setRequestHeader(headerName, headerValue);
31226             }
31227         }
31228         
31229         var _this = this;
31230         
31231         this.xhr.onload = function()
31232         {
31233             _this.xhrOnLoad(_this.xhr);
31234         }
31235         
31236         this.xhr.onerror = function()
31237         {
31238             _this.xhrOnError(_this.xhr);
31239         }
31240         
31241         var formData = new FormData();
31242
31243         formData.append('returnHTML', 'NO');
31244         
31245         if(crop){
31246             formData.append('crop', crop);
31247         }
31248         
31249         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31250             formData.append(this.paramName, file, file.name);
31251         }
31252         
31253         if(typeof(file.filename) != 'undefined'){
31254             formData.append('filename', file.filename);
31255         }
31256         
31257         if(typeof(file.mimetype) != 'undefined'){
31258             formData.append('mimetype', file.mimetype);
31259         }
31260         
31261         if(this.fireEvent('arrange', this, formData) != false){
31262             this.xhr.send(formData);
31263         };
31264     },
31265     
31266     xhrOnLoad : function(xhr)
31267     {
31268         if(this.loadMask){
31269             this.maskEl.unmask();
31270         }
31271         
31272         if (xhr.readyState !== 4) {
31273             this.fireEvent('exception', this, xhr);
31274             return;
31275         }
31276
31277         var response = Roo.decode(xhr.responseText);
31278         
31279         if(!response.success){
31280             this.fireEvent('exception', this, xhr);
31281             return;
31282         }
31283         
31284         var response = Roo.decode(xhr.responseText);
31285         
31286         this.fireEvent('upload', this, response);
31287         
31288     },
31289     
31290     xhrOnError : function()
31291     {
31292         if(this.loadMask){
31293             this.maskEl.unmask();
31294         }
31295         
31296         Roo.log('xhr on error');
31297         
31298         var response = Roo.decode(xhr.responseText);
31299           
31300         Roo.log(response);
31301         
31302     },
31303     
31304     prepare : function(file)
31305     {   
31306         if(this.loadMask){
31307             this.maskEl.mask(this.loadingText);
31308         }
31309         
31310         this.file = false;
31311         this.exif = {};
31312         
31313         if(typeof(file) === 'string'){
31314             this.loadCanvas(file);
31315             return;
31316         }
31317         
31318         if(!file || !this.urlAPI){
31319             return;
31320         }
31321         
31322         this.file = file;
31323         this.cropType = file.type;
31324         
31325         var _this = this;
31326         
31327         if(this.fireEvent('prepare', this, this.file) != false){
31328             
31329             var reader = new FileReader();
31330             
31331             reader.onload = function (e) {
31332                 if (e.target.error) {
31333                     Roo.log(e.target.error);
31334                     return;
31335                 }
31336                 
31337                 var buffer = e.target.result,
31338                     dataView = new DataView(buffer),
31339                     offset = 2,
31340                     maxOffset = dataView.byteLength - 4,
31341                     markerBytes,
31342                     markerLength;
31343                 
31344                 if (dataView.getUint16(0) === 0xffd8) {
31345                     while (offset < maxOffset) {
31346                         markerBytes = dataView.getUint16(offset);
31347                         
31348                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31349                             markerLength = dataView.getUint16(offset + 2) + 2;
31350                             if (offset + markerLength > dataView.byteLength) {
31351                                 Roo.log('Invalid meta data: Invalid segment size.');
31352                                 break;
31353                             }
31354                             
31355                             if(markerBytes == 0xffe1){
31356                                 _this.parseExifData(
31357                                     dataView,
31358                                     offset,
31359                                     markerLength
31360                                 );
31361                             }
31362                             
31363                             offset += markerLength;
31364                             
31365                             continue;
31366                         }
31367                         
31368                         break;
31369                     }
31370                     
31371                 }
31372                 
31373                 var url = _this.urlAPI.createObjectURL(_this.file);
31374                 
31375                 _this.loadCanvas(url);
31376                 
31377                 return;
31378             }
31379             
31380             reader.readAsArrayBuffer(this.file);
31381             
31382         }
31383         
31384     },
31385     
31386     parseExifData : function(dataView, offset, length)
31387     {
31388         var tiffOffset = offset + 10,
31389             littleEndian,
31390             dirOffset;
31391     
31392         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31393             // No Exif data, might be XMP data instead
31394             return;
31395         }
31396         
31397         // Check for the ASCII code for "Exif" (0x45786966):
31398         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31399             // No Exif data, might be XMP data instead
31400             return;
31401         }
31402         if (tiffOffset + 8 > dataView.byteLength) {
31403             Roo.log('Invalid Exif data: Invalid segment size.');
31404             return;
31405         }
31406         // Check for the two null bytes:
31407         if (dataView.getUint16(offset + 8) !== 0x0000) {
31408             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31409             return;
31410         }
31411         // Check the byte alignment:
31412         switch (dataView.getUint16(tiffOffset)) {
31413         case 0x4949:
31414             littleEndian = true;
31415             break;
31416         case 0x4D4D:
31417             littleEndian = false;
31418             break;
31419         default:
31420             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31421             return;
31422         }
31423         // Check for the TIFF tag marker (0x002A):
31424         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31425             Roo.log('Invalid Exif data: Missing TIFF marker.');
31426             return;
31427         }
31428         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31429         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31430         
31431         this.parseExifTags(
31432             dataView,
31433             tiffOffset,
31434             tiffOffset + dirOffset,
31435             littleEndian
31436         );
31437     },
31438     
31439     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31440     {
31441         var tagsNumber,
31442             dirEndOffset,
31443             i;
31444         if (dirOffset + 6 > dataView.byteLength) {
31445             Roo.log('Invalid Exif data: Invalid directory offset.');
31446             return;
31447         }
31448         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31449         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31450         if (dirEndOffset + 4 > dataView.byteLength) {
31451             Roo.log('Invalid Exif data: Invalid directory size.');
31452             return;
31453         }
31454         for (i = 0; i < tagsNumber; i += 1) {
31455             this.parseExifTag(
31456                 dataView,
31457                 tiffOffset,
31458                 dirOffset + 2 + 12 * i, // tag offset
31459                 littleEndian
31460             );
31461         }
31462         // Return the offset to the next directory:
31463         return dataView.getUint32(dirEndOffset, littleEndian);
31464     },
31465     
31466     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31467     {
31468         var tag = dataView.getUint16(offset, littleEndian);
31469         
31470         this.exif[tag] = this.getExifValue(
31471             dataView,
31472             tiffOffset,
31473             offset,
31474             dataView.getUint16(offset + 2, littleEndian), // tag type
31475             dataView.getUint32(offset + 4, littleEndian), // tag length
31476             littleEndian
31477         );
31478     },
31479     
31480     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31481     {
31482         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31483             tagSize,
31484             dataOffset,
31485             values,
31486             i,
31487             str,
31488             c;
31489     
31490         if (!tagType) {
31491             Roo.log('Invalid Exif data: Invalid tag type.');
31492             return;
31493         }
31494         
31495         tagSize = tagType.size * length;
31496         // Determine if the value is contained in the dataOffset bytes,
31497         // or if the value at the dataOffset is a pointer to the actual data:
31498         dataOffset = tagSize > 4 ?
31499                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31500         if (dataOffset + tagSize > dataView.byteLength) {
31501             Roo.log('Invalid Exif data: Invalid data offset.');
31502             return;
31503         }
31504         if (length === 1) {
31505             return tagType.getValue(dataView, dataOffset, littleEndian);
31506         }
31507         values = [];
31508         for (i = 0; i < length; i += 1) {
31509             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31510         }
31511         
31512         if (tagType.ascii) {
31513             str = '';
31514             // Concatenate the chars:
31515             for (i = 0; i < values.length; i += 1) {
31516                 c = values[i];
31517                 // Ignore the terminating NULL byte(s):
31518                 if (c === '\u0000') {
31519                     break;
31520                 }
31521                 str += c;
31522             }
31523             return str;
31524         }
31525         return values;
31526     }
31527     
31528 });
31529
31530 Roo.apply(Roo.bootstrap.UploadCropbox, {
31531     tags : {
31532         'Orientation': 0x0112
31533     },
31534     
31535     Orientation: {
31536             1: 0, //'top-left',
31537 //            2: 'top-right',
31538             3: 180, //'bottom-right',
31539 //            4: 'bottom-left',
31540 //            5: 'left-top',
31541             6: 90, //'right-top',
31542 //            7: 'right-bottom',
31543             8: 270 //'left-bottom'
31544     },
31545     
31546     exifTagTypes : {
31547         // byte, 8-bit unsigned int:
31548         1: {
31549             getValue: function (dataView, dataOffset) {
31550                 return dataView.getUint8(dataOffset);
31551             },
31552             size: 1
31553         },
31554         // ascii, 8-bit byte:
31555         2: {
31556             getValue: function (dataView, dataOffset) {
31557                 return String.fromCharCode(dataView.getUint8(dataOffset));
31558             },
31559             size: 1,
31560             ascii: true
31561         },
31562         // short, 16 bit int:
31563         3: {
31564             getValue: function (dataView, dataOffset, littleEndian) {
31565                 return dataView.getUint16(dataOffset, littleEndian);
31566             },
31567             size: 2
31568         },
31569         // long, 32 bit int:
31570         4: {
31571             getValue: function (dataView, dataOffset, littleEndian) {
31572                 return dataView.getUint32(dataOffset, littleEndian);
31573             },
31574             size: 4
31575         },
31576         // rational = two long values, first is numerator, second is denominator:
31577         5: {
31578             getValue: function (dataView, dataOffset, littleEndian) {
31579                 return dataView.getUint32(dataOffset, littleEndian) /
31580                     dataView.getUint32(dataOffset + 4, littleEndian);
31581             },
31582             size: 8
31583         },
31584         // slong, 32 bit signed int:
31585         9: {
31586             getValue: function (dataView, dataOffset, littleEndian) {
31587                 return dataView.getInt32(dataOffset, littleEndian);
31588             },
31589             size: 4
31590         },
31591         // srational, two slongs, first is numerator, second is denominator:
31592         10: {
31593             getValue: function (dataView, dataOffset, littleEndian) {
31594                 return dataView.getInt32(dataOffset, littleEndian) /
31595                     dataView.getInt32(dataOffset + 4, littleEndian);
31596             },
31597             size: 8
31598         }
31599     },
31600     
31601     footer : {
31602         STANDARD : [
31603             {
31604                 tag : 'div',
31605                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31606                 action : 'rotate-left',
31607                 cn : [
31608                     {
31609                         tag : 'button',
31610                         cls : 'btn btn-default',
31611                         html : '<i class="fa fa-undo"></i>'
31612                     }
31613                 ]
31614             },
31615             {
31616                 tag : 'div',
31617                 cls : 'btn-group roo-upload-cropbox-picture',
31618                 action : 'picture',
31619                 cn : [
31620                     {
31621                         tag : 'button',
31622                         cls : 'btn btn-default',
31623                         html : '<i class="fa fa-picture-o"></i>'
31624                     }
31625                 ]
31626             },
31627             {
31628                 tag : 'div',
31629                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31630                 action : 'rotate-right',
31631                 cn : [
31632                     {
31633                         tag : 'button',
31634                         cls : 'btn btn-default',
31635                         html : '<i class="fa fa-repeat"></i>'
31636                     }
31637                 ]
31638             }
31639         ],
31640         DOCUMENT : [
31641             {
31642                 tag : 'div',
31643                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31644                 action : 'rotate-left',
31645                 cn : [
31646                     {
31647                         tag : 'button',
31648                         cls : 'btn btn-default',
31649                         html : '<i class="fa fa-undo"></i>'
31650                     }
31651                 ]
31652             },
31653             {
31654                 tag : 'div',
31655                 cls : 'btn-group roo-upload-cropbox-download',
31656                 action : 'download',
31657                 cn : [
31658                     {
31659                         tag : 'button',
31660                         cls : 'btn btn-default',
31661                         html : '<i class="fa fa-download"></i>'
31662                     }
31663                 ]
31664             },
31665             {
31666                 tag : 'div',
31667                 cls : 'btn-group roo-upload-cropbox-crop',
31668                 action : 'crop',
31669                 cn : [
31670                     {
31671                         tag : 'button',
31672                         cls : 'btn btn-default',
31673                         html : '<i class="fa fa-crop"></i>'
31674                     }
31675                 ]
31676             },
31677             {
31678                 tag : 'div',
31679                 cls : 'btn-group roo-upload-cropbox-trash',
31680                 action : 'trash',
31681                 cn : [
31682                     {
31683                         tag : 'button',
31684                         cls : 'btn btn-default',
31685                         html : '<i class="fa fa-trash"></i>'
31686                     }
31687                 ]
31688             },
31689             {
31690                 tag : 'div',
31691                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31692                 action : 'rotate-right',
31693                 cn : [
31694                     {
31695                         tag : 'button',
31696                         cls : 'btn btn-default',
31697                         html : '<i class="fa fa-repeat"></i>'
31698                     }
31699                 ]
31700             }
31701         ],
31702         ROTATOR : [
31703             {
31704                 tag : 'div',
31705                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31706                 action : 'rotate-left',
31707                 cn : [
31708                     {
31709                         tag : 'button',
31710                         cls : 'btn btn-default',
31711                         html : '<i class="fa fa-undo"></i>'
31712                     }
31713                 ]
31714             },
31715             {
31716                 tag : 'div',
31717                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31718                 action : 'rotate-right',
31719                 cn : [
31720                     {
31721                         tag : 'button',
31722                         cls : 'btn btn-default',
31723                         html : '<i class="fa fa-repeat"></i>'
31724                     }
31725                 ]
31726             }
31727         ]
31728     }
31729 });
31730
31731 /*
31732 * Licence: LGPL
31733 */
31734
31735 /**
31736  * @class Roo.bootstrap.DocumentManager
31737  * @extends Roo.bootstrap.Component
31738  * Bootstrap DocumentManager class
31739  * @cfg {String} paramName default 'imageUpload'
31740  * @cfg {String} toolTipName default 'filename'
31741  * @cfg {String} method default POST
31742  * @cfg {String} url action url
31743  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31744  * @cfg {Boolean} multiple multiple upload default true
31745  * @cfg {Number} thumbSize default 300
31746  * @cfg {String} fieldLabel
31747  * @cfg {Number} labelWidth default 4
31748  * @cfg {String} labelAlign (left|top) default left
31749  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31750 * @cfg {Number} labellg set the width of label (1-12)
31751  * @cfg {Number} labelmd set the width of label (1-12)
31752  * @cfg {Number} labelsm set the width of label (1-12)
31753  * @cfg {Number} labelxs set the width of label (1-12)
31754  * 
31755  * @constructor
31756  * Create a new DocumentManager
31757  * @param {Object} config The config object
31758  */
31759
31760 Roo.bootstrap.DocumentManager = function(config){
31761     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31762     
31763     this.files = [];
31764     this.delegates = [];
31765     
31766     this.addEvents({
31767         /**
31768          * @event initial
31769          * Fire when initial the DocumentManager
31770          * @param {Roo.bootstrap.DocumentManager} this
31771          */
31772         "initial" : true,
31773         /**
31774          * @event inspect
31775          * inspect selected file
31776          * @param {Roo.bootstrap.DocumentManager} this
31777          * @param {File} file
31778          */
31779         "inspect" : true,
31780         /**
31781          * @event exception
31782          * Fire when xhr load exception
31783          * @param {Roo.bootstrap.DocumentManager} this
31784          * @param {XMLHttpRequest} xhr
31785          */
31786         "exception" : true,
31787         /**
31788          * @event afterupload
31789          * Fire when xhr load exception
31790          * @param {Roo.bootstrap.DocumentManager} this
31791          * @param {XMLHttpRequest} xhr
31792          */
31793         "afterupload" : true,
31794         /**
31795          * @event prepare
31796          * prepare the form data
31797          * @param {Roo.bootstrap.DocumentManager} this
31798          * @param {Object} formData
31799          */
31800         "prepare" : true,
31801         /**
31802          * @event remove
31803          * Fire when remove the file
31804          * @param {Roo.bootstrap.DocumentManager} this
31805          * @param {Object} file
31806          */
31807         "remove" : true,
31808         /**
31809          * @event refresh
31810          * Fire after refresh the file
31811          * @param {Roo.bootstrap.DocumentManager} this
31812          */
31813         "refresh" : true,
31814         /**
31815          * @event click
31816          * Fire after click the image
31817          * @param {Roo.bootstrap.DocumentManager} this
31818          * @param {Object} file
31819          */
31820         "click" : true,
31821         /**
31822          * @event edit
31823          * Fire when upload a image and editable set to true
31824          * @param {Roo.bootstrap.DocumentManager} this
31825          * @param {Object} file
31826          */
31827         "edit" : true,
31828         /**
31829          * @event beforeselectfile
31830          * Fire before select file
31831          * @param {Roo.bootstrap.DocumentManager} this
31832          */
31833         "beforeselectfile" : true,
31834         /**
31835          * @event process
31836          * Fire before process file
31837          * @param {Roo.bootstrap.DocumentManager} this
31838          * @param {Object} file
31839          */
31840         "process" : true,
31841         /**
31842          * @event previewrendered
31843          * Fire when preview rendered
31844          * @param {Roo.bootstrap.DocumentManager} this
31845          * @param {Object} file
31846          */
31847         "previewrendered" : true,
31848         /**
31849          */
31850         "previewResize" : true
31851         
31852     });
31853 };
31854
31855 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31856     
31857     boxes : 0,
31858     inputName : '',
31859     thumbSize : 300,
31860     multiple : true,
31861     files : false,
31862     method : 'POST',
31863     url : '',
31864     paramName : 'imageUpload',
31865     toolTipName : 'filename',
31866     fieldLabel : '',
31867     labelWidth : 4,
31868     labelAlign : 'left',
31869     editable : true,
31870     delegates : false,
31871     xhr : false, 
31872     
31873     labellg : 0,
31874     labelmd : 0,
31875     labelsm : 0,
31876     labelxs : 0,
31877     
31878     getAutoCreate : function()
31879     {   
31880         var managerWidget = {
31881             tag : 'div',
31882             cls : 'roo-document-manager',
31883             cn : [
31884                 {
31885                     tag : 'input',
31886                     cls : 'roo-document-manager-selector',
31887                     type : 'file'
31888                 },
31889                 {
31890                     tag : 'div',
31891                     cls : 'roo-document-manager-uploader',
31892                     cn : [
31893                         {
31894                             tag : 'div',
31895                             cls : 'roo-document-manager-upload-btn',
31896                             html : '<i class="fa fa-plus"></i>'
31897                         }
31898                     ]
31899                     
31900                 }
31901             ]
31902         };
31903         
31904         var content = [
31905             {
31906                 tag : 'div',
31907                 cls : 'column col-md-12',
31908                 cn : managerWidget
31909             }
31910         ];
31911         
31912         if(this.fieldLabel.length){
31913             
31914             content = [
31915                 {
31916                     tag : 'div',
31917                     cls : 'column col-md-12',
31918                     html : this.fieldLabel
31919                 },
31920                 {
31921                     tag : 'div',
31922                     cls : 'column col-md-12',
31923                     cn : managerWidget
31924                 }
31925             ];
31926
31927             if(this.labelAlign == 'left'){
31928                 content = [
31929                     {
31930                         tag : 'div',
31931                         cls : 'column',
31932                         html : this.fieldLabel
31933                     },
31934                     {
31935                         tag : 'div',
31936                         cls : 'column',
31937                         cn : managerWidget
31938                     }
31939                 ];
31940                 
31941                 if(this.labelWidth > 12){
31942                     content[0].style = "width: " + this.labelWidth + 'px';
31943                 }
31944
31945                 if(this.labelWidth < 13 && this.labelmd == 0){
31946                     this.labelmd = this.labelWidth;
31947                 }
31948
31949                 if(this.labellg > 0){
31950                     content[0].cls += ' col-lg-' + this.labellg;
31951                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31952                 }
31953
31954                 if(this.labelmd > 0){
31955                     content[0].cls += ' col-md-' + this.labelmd;
31956                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31957                 }
31958
31959                 if(this.labelsm > 0){
31960                     content[0].cls += ' col-sm-' + this.labelsm;
31961                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31962                 }
31963
31964                 if(this.labelxs > 0){
31965                     content[0].cls += ' col-xs-' + this.labelxs;
31966                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31967                 }
31968                 
31969             }
31970         }
31971         
31972         var cfg = {
31973             tag : 'div',
31974             cls : 'row clearfix',
31975             cn : content
31976         };
31977         
31978         return cfg;
31979         
31980     },
31981     
31982     initEvents : function()
31983     {
31984         this.managerEl = this.el.select('.roo-document-manager', true).first();
31985         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31986         
31987         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31988         this.selectorEl.hide();
31989         
31990         if(this.multiple){
31991             this.selectorEl.attr('multiple', 'multiple');
31992         }
31993         
31994         this.selectorEl.on('change', this.onFileSelected, this);
31995         
31996         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31997         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31998         
31999         this.uploader.on('click', this.onUploaderClick, this);
32000         
32001         this.renderProgressDialog();
32002         
32003         var _this = this;
32004         
32005         window.addEventListener("resize", function() { _this.refresh(); } );
32006         
32007         this.fireEvent('initial', this);
32008     },
32009     
32010     renderProgressDialog : function()
32011     {
32012         var _this = this;
32013         
32014         this.progressDialog = new Roo.bootstrap.Modal({
32015             cls : 'roo-document-manager-progress-dialog',
32016             allow_close : false,
32017             animate : false,
32018             title : '',
32019             buttons : [
32020                 {
32021                     name  :'cancel',
32022                     weight : 'danger',
32023                     html : 'Cancel'
32024                 }
32025             ], 
32026             listeners : { 
32027                 btnclick : function() {
32028                     _this.uploadCancel();
32029                     this.hide();
32030                 }
32031             }
32032         });
32033          
32034         this.progressDialog.render(Roo.get(document.body));
32035          
32036         this.progress = new Roo.bootstrap.Progress({
32037             cls : 'roo-document-manager-progress',
32038             active : true,
32039             striped : true
32040         });
32041         
32042         this.progress.render(this.progressDialog.getChildContainer());
32043         
32044         this.progressBar = new Roo.bootstrap.ProgressBar({
32045             cls : 'roo-document-manager-progress-bar',
32046             aria_valuenow : 0,
32047             aria_valuemin : 0,
32048             aria_valuemax : 12,
32049             panel : 'success'
32050         });
32051         
32052         this.progressBar.render(this.progress.getChildContainer());
32053     },
32054     
32055     onUploaderClick : function(e)
32056     {
32057         e.preventDefault();
32058      
32059         if(this.fireEvent('beforeselectfile', this) != false){
32060             this.selectorEl.dom.click();
32061         }
32062         
32063     },
32064     
32065     onFileSelected : function(e)
32066     {
32067         e.preventDefault();
32068         
32069         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32070             return;
32071         }
32072         
32073         Roo.each(this.selectorEl.dom.files, function(file){
32074             if(this.fireEvent('inspect', this, file) != false){
32075                 this.files.push(file);
32076             }
32077         }, this);
32078         
32079         this.queue();
32080         
32081     },
32082     
32083     queue : function()
32084     {
32085         this.selectorEl.dom.value = '';
32086         
32087         if(!this.files || !this.files.length){
32088             return;
32089         }
32090         
32091         if(this.boxes > 0 && this.files.length > this.boxes){
32092             this.files = this.files.slice(0, this.boxes);
32093         }
32094         
32095         this.uploader.show();
32096         
32097         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32098             this.uploader.hide();
32099         }
32100         
32101         var _this = this;
32102         
32103         var files = [];
32104         
32105         var docs = [];
32106         
32107         Roo.each(this.files, function(file){
32108             
32109             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32110                 var f = this.renderPreview(file);
32111                 files.push(f);
32112                 return;
32113             }
32114             
32115             if(file.type.indexOf('image') != -1){
32116                 this.delegates.push(
32117                     (function(){
32118                         _this.process(file);
32119                     }).createDelegate(this)
32120                 );
32121         
32122                 return;
32123             }
32124             
32125             docs.push(
32126                 (function(){
32127                     _this.process(file);
32128                 }).createDelegate(this)
32129             );
32130             
32131         }, this);
32132         
32133         this.files = files;
32134         
32135         this.delegates = this.delegates.concat(docs);
32136         
32137         if(!this.delegates.length){
32138             this.refresh();
32139             return;
32140         }
32141         
32142         this.progressBar.aria_valuemax = this.delegates.length;
32143         
32144         this.arrange();
32145         
32146         return;
32147     },
32148     
32149     arrange : function()
32150     {
32151         if(!this.delegates.length){
32152             this.progressDialog.hide();
32153             this.refresh();
32154             return;
32155         }
32156         
32157         var delegate = this.delegates.shift();
32158         
32159         this.progressDialog.show();
32160         
32161         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32162         
32163         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32164         
32165         delegate();
32166     },
32167     
32168     refresh : function()
32169     {
32170         this.uploader.show();
32171         
32172         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32173             this.uploader.hide();
32174         }
32175         
32176         Roo.isTouch ? this.closable(false) : this.closable(true);
32177         
32178         this.fireEvent('refresh', this);
32179     },
32180     
32181     onRemove : function(e, el, o)
32182     {
32183         e.preventDefault();
32184         
32185         this.fireEvent('remove', this, o);
32186         
32187     },
32188     
32189     remove : function(o)
32190     {
32191         var files = [];
32192         
32193         Roo.each(this.files, function(file){
32194             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32195                 files.push(file);
32196                 return;
32197             }
32198
32199             o.target.remove();
32200
32201         }, this);
32202         
32203         this.files = files;
32204         
32205         this.refresh();
32206     },
32207     
32208     clear : function()
32209     {
32210         Roo.each(this.files, function(file){
32211             if(!file.target){
32212                 return;
32213             }
32214             
32215             file.target.remove();
32216
32217         }, this);
32218         
32219         this.files = [];
32220         
32221         this.refresh();
32222     },
32223     
32224     onClick : function(e, el, o)
32225     {
32226         e.preventDefault();
32227         
32228         this.fireEvent('click', this, o);
32229         
32230     },
32231     
32232     closable : function(closable)
32233     {
32234         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32235             
32236             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32237             
32238             if(closable){
32239                 el.show();
32240                 return;
32241             }
32242             
32243             el.hide();
32244             
32245         }, this);
32246     },
32247     
32248     xhrOnLoad : function(xhr)
32249     {
32250         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32251             el.remove();
32252         }, this);
32253         
32254         if (xhr.readyState !== 4) {
32255             this.arrange();
32256             this.fireEvent('exception', this, xhr);
32257             return;
32258         }
32259
32260         var response = Roo.decode(xhr.responseText);
32261         
32262         if(!response.success){
32263             this.arrange();
32264             this.fireEvent('exception', this, xhr);
32265             return;
32266         }
32267         
32268         var file = this.renderPreview(response.data);
32269         
32270         this.files.push(file);
32271         
32272         this.arrange();
32273         
32274         this.fireEvent('afterupload', this, xhr);
32275         
32276     },
32277     
32278     xhrOnError : function(xhr)
32279     {
32280         Roo.log('xhr on error');
32281         
32282         var response = Roo.decode(xhr.responseText);
32283           
32284         Roo.log(response);
32285         
32286         this.arrange();
32287     },
32288     
32289     process : function(file)
32290     {
32291         if(this.fireEvent('process', this, file) !== false){
32292             if(this.editable && file.type.indexOf('image') != -1){
32293                 this.fireEvent('edit', this, file);
32294                 return;
32295             }
32296
32297             this.uploadStart(file, false);
32298
32299             return;
32300         }
32301         
32302     },
32303     
32304     uploadStart : function(file, crop)
32305     {
32306         this.xhr = new XMLHttpRequest();
32307         
32308         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32309             this.arrange();
32310             return;
32311         }
32312         
32313         file.xhr = this.xhr;
32314             
32315         this.managerEl.createChild({
32316             tag : 'div',
32317             cls : 'roo-document-manager-loading',
32318             cn : [
32319                 {
32320                     tag : 'div',
32321                     tooltip : file.name,
32322                     cls : 'roo-document-manager-thumb',
32323                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32324                 }
32325             ]
32326
32327         });
32328
32329         this.xhr.open(this.method, this.url, true);
32330         
32331         var headers = {
32332             "Accept": "application/json",
32333             "Cache-Control": "no-cache",
32334             "X-Requested-With": "XMLHttpRequest"
32335         };
32336         
32337         for (var headerName in headers) {
32338             var headerValue = headers[headerName];
32339             if (headerValue) {
32340                 this.xhr.setRequestHeader(headerName, headerValue);
32341             }
32342         }
32343         
32344         var _this = this;
32345         
32346         this.xhr.onload = function()
32347         {
32348             _this.xhrOnLoad(_this.xhr);
32349         }
32350         
32351         this.xhr.onerror = function()
32352         {
32353             _this.xhrOnError(_this.xhr);
32354         }
32355         
32356         var formData = new FormData();
32357
32358         formData.append('returnHTML', 'NO');
32359         
32360         if(crop){
32361             formData.append('crop', crop);
32362         }
32363         
32364         formData.append(this.paramName, file, file.name);
32365         
32366         var options = {
32367             file : file, 
32368             manually : false
32369         };
32370         
32371         if(this.fireEvent('prepare', this, formData, options) != false){
32372             
32373             if(options.manually){
32374                 return;
32375             }
32376             
32377             this.xhr.send(formData);
32378             return;
32379         };
32380         
32381         this.uploadCancel();
32382     },
32383     
32384     uploadCancel : function()
32385     {
32386         if (this.xhr) {
32387             this.xhr.abort();
32388         }
32389         
32390         this.delegates = [];
32391         
32392         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32393             el.remove();
32394         }, this);
32395         
32396         this.arrange();
32397     },
32398     
32399     renderPreview : function(file)
32400     {
32401         if(typeof(file.target) != 'undefined' && file.target){
32402             return file;
32403         }
32404         
32405         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32406         
32407         var previewEl = this.managerEl.createChild({
32408             tag : 'div',
32409             cls : 'roo-document-manager-preview',
32410             cn : [
32411                 {
32412                     tag : 'div',
32413                     tooltip : file[this.toolTipName],
32414                     cls : 'roo-document-manager-thumb',
32415                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32416                 },
32417                 {
32418                     tag : 'button',
32419                     cls : 'close',
32420                     html : '<i class="fa fa-times-circle"></i>'
32421                 }
32422             ]
32423         });
32424
32425         var close = previewEl.select('button.close', true).first();
32426
32427         close.on('click', this.onRemove, this, file);
32428
32429         file.target = previewEl;
32430
32431         var image = previewEl.select('img', true).first();
32432         
32433         var _this = this;
32434         
32435         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32436         
32437         image.on('click', this.onClick, this, file);
32438         
32439         this.fireEvent('previewrendered', this, file);
32440         
32441         return file;
32442         
32443     },
32444     
32445     onPreviewLoad : function(file, image)
32446     {
32447         if(typeof(file.target) == 'undefined' || !file.target){
32448             return;
32449         }
32450         
32451         var width = image.dom.naturalWidth || image.dom.width;
32452         var height = image.dom.naturalHeight || image.dom.height;
32453         
32454         if(!this.previewResize) {
32455             return;
32456         }
32457         
32458         if(width > height){
32459             file.target.addClass('wide');
32460             return;
32461         }
32462         
32463         file.target.addClass('tall');
32464         return;
32465         
32466     },
32467     
32468     uploadFromSource : function(file, crop)
32469     {
32470         this.xhr = new XMLHttpRequest();
32471         
32472         this.managerEl.createChild({
32473             tag : 'div',
32474             cls : 'roo-document-manager-loading',
32475             cn : [
32476                 {
32477                     tag : 'div',
32478                     tooltip : file.name,
32479                     cls : 'roo-document-manager-thumb',
32480                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32481                 }
32482             ]
32483
32484         });
32485
32486         this.xhr.open(this.method, this.url, true);
32487         
32488         var headers = {
32489             "Accept": "application/json",
32490             "Cache-Control": "no-cache",
32491             "X-Requested-With": "XMLHttpRequest"
32492         };
32493         
32494         for (var headerName in headers) {
32495             var headerValue = headers[headerName];
32496             if (headerValue) {
32497                 this.xhr.setRequestHeader(headerName, headerValue);
32498             }
32499         }
32500         
32501         var _this = this;
32502         
32503         this.xhr.onload = function()
32504         {
32505             _this.xhrOnLoad(_this.xhr);
32506         }
32507         
32508         this.xhr.onerror = function()
32509         {
32510             _this.xhrOnError(_this.xhr);
32511         }
32512         
32513         var formData = new FormData();
32514
32515         formData.append('returnHTML', 'NO');
32516         
32517         formData.append('crop', crop);
32518         
32519         if(typeof(file.filename) != 'undefined'){
32520             formData.append('filename', file.filename);
32521         }
32522         
32523         if(typeof(file.mimetype) != 'undefined'){
32524             formData.append('mimetype', file.mimetype);
32525         }
32526         
32527         Roo.log(formData);
32528         
32529         if(this.fireEvent('prepare', this, formData) != false){
32530             this.xhr.send(formData);
32531         };
32532     }
32533 });
32534
32535 /*
32536 * Licence: LGPL
32537 */
32538
32539 /**
32540  * @class Roo.bootstrap.DocumentViewer
32541  * @extends Roo.bootstrap.Component
32542  * Bootstrap DocumentViewer class
32543  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32544  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32545  * 
32546  * @constructor
32547  * Create a new DocumentViewer
32548  * @param {Object} config The config object
32549  */
32550
32551 Roo.bootstrap.DocumentViewer = function(config){
32552     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32553     
32554     this.addEvents({
32555         /**
32556          * @event initial
32557          * Fire after initEvent
32558          * @param {Roo.bootstrap.DocumentViewer} this
32559          */
32560         "initial" : true,
32561         /**
32562          * @event click
32563          * Fire after click
32564          * @param {Roo.bootstrap.DocumentViewer} this
32565          */
32566         "click" : true,
32567         /**
32568          * @event download
32569          * Fire after download button
32570          * @param {Roo.bootstrap.DocumentViewer} this
32571          */
32572         "download" : true,
32573         /**
32574          * @event trash
32575          * Fire after trash button
32576          * @param {Roo.bootstrap.DocumentViewer} this
32577          */
32578         "trash" : true
32579         
32580     });
32581 };
32582
32583 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32584     
32585     showDownload : true,
32586     
32587     showTrash : true,
32588     
32589     getAutoCreate : function()
32590     {
32591         var cfg = {
32592             tag : 'div',
32593             cls : 'roo-document-viewer',
32594             cn : [
32595                 {
32596                     tag : 'div',
32597                     cls : 'roo-document-viewer-body',
32598                     cn : [
32599                         {
32600                             tag : 'div',
32601                             cls : 'roo-document-viewer-thumb',
32602                             cn : [
32603                                 {
32604                                     tag : 'img',
32605                                     cls : 'roo-document-viewer-image'
32606                                 }
32607                             ]
32608                         }
32609                     ]
32610                 },
32611                 {
32612                     tag : 'div',
32613                     cls : 'roo-document-viewer-footer',
32614                     cn : {
32615                         tag : 'div',
32616                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32617                         cn : [
32618                             {
32619                                 tag : 'div',
32620                                 cls : 'btn-group roo-document-viewer-download',
32621                                 cn : [
32622                                     {
32623                                         tag : 'button',
32624                                         cls : 'btn btn-default',
32625                                         html : '<i class="fa fa-download"></i>'
32626                                     }
32627                                 ]
32628                             },
32629                             {
32630                                 tag : 'div',
32631                                 cls : 'btn-group roo-document-viewer-trash',
32632                                 cn : [
32633                                     {
32634                                         tag : 'button',
32635                                         cls : 'btn btn-default',
32636                                         html : '<i class="fa fa-trash"></i>'
32637                                     }
32638                                 ]
32639                             }
32640                         ]
32641                     }
32642                 }
32643             ]
32644         };
32645         
32646         return cfg;
32647     },
32648     
32649     initEvents : function()
32650     {
32651         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32652         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32653         
32654         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32655         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32656         
32657         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32658         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32659         
32660         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32661         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32662         
32663         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32664         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32665         
32666         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32667         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32668         
32669         this.bodyEl.on('click', this.onClick, this);
32670         this.downloadBtn.on('click', this.onDownload, this);
32671         this.trashBtn.on('click', this.onTrash, this);
32672         
32673         this.downloadBtn.hide();
32674         this.trashBtn.hide();
32675         
32676         if(this.showDownload){
32677             this.downloadBtn.show();
32678         }
32679         
32680         if(this.showTrash){
32681             this.trashBtn.show();
32682         }
32683         
32684         if(!this.showDownload && !this.showTrash) {
32685             this.footerEl.hide();
32686         }
32687         
32688     },
32689     
32690     initial : function()
32691     {
32692         this.fireEvent('initial', this);
32693         
32694     },
32695     
32696     onClick : function(e)
32697     {
32698         e.preventDefault();
32699         
32700         this.fireEvent('click', this);
32701     },
32702     
32703     onDownload : function(e)
32704     {
32705         e.preventDefault();
32706         
32707         this.fireEvent('download', this);
32708     },
32709     
32710     onTrash : function(e)
32711     {
32712         e.preventDefault();
32713         
32714         this.fireEvent('trash', this);
32715     }
32716     
32717 });
32718 /*
32719  * - LGPL
32720  *
32721  * nav progress bar
32722  * 
32723  */
32724
32725 /**
32726  * @class Roo.bootstrap.NavProgressBar
32727  * @extends Roo.bootstrap.Component
32728  * Bootstrap NavProgressBar class
32729  * 
32730  * @constructor
32731  * Create a new nav progress bar
32732  * @param {Object} config The config object
32733  */
32734
32735 Roo.bootstrap.NavProgressBar = function(config){
32736     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32737
32738     this.bullets = this.bullets || [];
32739    
32740 //    Roo.bootstrap.NavProgressBar.register(this);
32741      this.addEvents({
32742         /**
32743              * @event changed
32744              * Fires when the active item changes
32745              * @param {Roo.bootstrap.NavProgressBar} this
32746              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32747              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32748          */
32749         'changed': true
32750      });
32751     
32752 };
32753
32754 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32755     
32756     bullets : [],
32757     barItems : [],
32758     
32759     getAutoCreate : function()
32760     {
32761         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32762         
32763         cfg = {
32764             tag : 'div',
32765             cls : 'roo-navigation-bar-group',
32766             cn : [
32767                 {
32768                     tag : 'div',
32769                     cls : 'roo-navigation-top-bar'
32770                 },
32771                 {
32772                     tag : 'div',
32773                     cls : 'roo-navigation-bullets-bar',
32774                     cn : [
32775                         {
32776                             tag : 'ul',
32777                             cls : 'roo-navigation-bar'
32778                         }
32779                     ]
32780                 },
32781                 
32782                 {
32783                     tag : 'div',
32784                     cls : 'roo-navigation-bottom-bar'
32785                 }
32786             ]
32787             
32788         };
32789         
32790         return cfg;
32791         
32792     },
32793     
32794     initEvents: function() 
32795     {
32796         
32797     },
32798     
32799     onRender : function(ct, position) 
32800     {
32801         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32802         
32803         if(this.bullets.length){
32804             Roo.each(this.bullets, function(b){
32805                this.addItem(b);
32806             }, this);
32807         }
32808         
32809         this.format();
32810         
32811     },
32812     
32813     addItem : function(cfg)
32814     {
32815         var item = new Roo.bootstrap.NavProgressItem(cfg);
32816         
32817         item.parentId = this.id;
32818         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32819         
32820         if(cfg.html){
32821             var top = new Roo.bootstrap.Element({
32822                 tag : 'div',
32823                 cls : 'roo-navigation-bar-text'
32824             });
32825             
32826             var bottom = new Roo.bootstrap.Element({
32827                 tag : 'div',
32828                 cls : 'roo-navigation-bar-text'
32829             });
32830             
32831             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32832             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32833             
32834             var topText = new Roo.bootstrap.Element({
32835                 tag : 'span',
32836                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32837             });
32838             
32839             var bottomText = new Roo.bootstrap.Element({
32840                 tag : 'span',
32841                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32842             });
32843             
32844             topText.onRender(top.el, null);
32845             bottomText.onRender(bottom.el, null);
32846             
32847             item.topEl = top;
32848             item.bottomEl = bottom;
32849         }
32850         
32851         this.barItems.push(item);
32852         
32853         return item;
32854     },
32855     
32856     getActive : function()
32857     {
32858         var active = false;
32859         
32860         Roo.each(this.barItems, function(v){
32861             
32862             if (!v.isActive()) {
32863                 return;
32864             }
32865             
32866             active = v;
32867             return false;
32868             
32869         });
32870         
32871         return active;
32872     },
32873     
32874     setActiveItem : function(item)
32875     {
32876         var prev = false;
32877         
32878         Roo.each(this.barItems, function(v){
32879             if (v.rid == item.rid) {
32880                 return ;
32881             }
32882             
32883             if (v.isActive()) {
32884                 v.setActive(false);
32885                 prev = v;
32886             }
32887         });
32888
32889         item.setActive(true);
32890         
32891         this.fireEvent('changed', this, item, prev);
32892     },
32893     
32894     getBarItem: function(rid)
32895     {
32896         var ret = false;
32897         
32898         Roo.each(this.barItems, function(e) {
32899             if (e.rid != rid) {
32900                 return;
32901             }
32902             
32903             ret =  e;
32904             return false;
32905         });
32906         
32907         return ret;
32908     },
32909     
32910     indexOfItem : function(item)
32911     {
32912         var index = false;
32913         
32914         Roo.each(this.barItems, function(v, i){
32915             
32916             if (v.rid != item.rid) {
32917                 return;
32918             }
32919             
32920             index = i;
32921             return false
32922         });
32923         
32924         return index;
32925     },
32926     
32927     setActiveNext : function()
32928     {
32929         var i = this.indexOfItem(this.getActive());
32930         
32931         if (i > this.barItems.length) {
32932             return;
32933         }
32934         
32935         this.setActiveItem(this.barItems[i+1]);
32936     },
32937     
32938     setActivePrev : function()
32939     {
32940         var i = this.indexOfItem(this.getActive());
32941         
32942         if (i  < 1) {
32943             return;
32944         }
32945         
32946         this.setActiveItem(this.barItems[i-1]);
32947     },
32948     
32949     format : function()
32950     {
32951         if(!this.barItems.length){
32952             return;
32953         }
32954      
32955         var width = 100 / this.barItems.length;
32956         
32957         Roo.each(this.barItems, function(i){
32958             i.el.setStyle('width', width + '%');
32959             i.topEl.el.setStyle('width', width + '%');
32960             i.bottomEl.el.setStyle('width', width + '%');
32961         }, this);
32962         
32963     }
32964     
32965 });
32966 /*
32967  * - LGPL
32968  *
32969  * Nav Progress Item
32970  * 
32971  */
32972
32973 /**
32974  * @class Roo.bootstrap.NavProgressItem
32975  * @extends Roo.bootstrap.Component
32976  * Bootstrap NavProgressItem class
32977  * @cfg {String} rid the reference id
32978  * @cfg {Boolean} active (true|false) Is item active default false
32979  * @cfg {Boolean} disabled (true|false) Is item active default false
32980  * @cfg {String} html
32981  * @cfg {String} position (top|bottom) text position default bottom
32982  * @cfg {String} icon show icon instead of number
32983  * 
32984  * @constructor
32985  * Create a new NavProgressItem
32986  * @param {Object} config The config object
32987  */
32988 Roo.bootstrap.NavProgressItem = function(config){
32989     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32990     this.addEvents({
32991         // raw events
32992         /**
32993          * @event click
32994          * The raw click event for the entire grid.
32995          * @param {Roo.bootstrap.NavProgressItem} this
32996          * @param {Roo.EventObject} e
32997          */
32998         "click" : true
32999     });
33000    
33001 };
33002
33003 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33004     
33005     rid : '',
33006     active : false,
33007     disabled : false,
33008     html : '',
33009     position : 'bottom',
33010     icon : false,
33011     
33012     getAutoCreate : function()
33013     {
33014         var iconCls = 'roo-navigation-bar-item-icon';
33015         
33016         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33017         
33018         var cfg = {
33019             tag: 'li',
33020             cls: 'roo-navigation-bar-item',
33021             cn : [
33022                 {
33023                     tag : 'i',
33024                     cls : iconCls
33025                 }
33026             ]
33027         };
33028         
33029         if(this.active){
33030             cfg.cls += ' active';
33031         }
33032         if(this.disabled){
33033             cfg.cls += ' disabled';
33034         }
33035         
33036         return cfg;
33037     },
33038     
33039     disable : function()
33040     {
33041         this.setDisabled(true);
33042     },
33043     
33044     enable : function()
33045     {
33046         this.setDisabled(false);
33047     },
33048     
33049     initEvents: function() 
33050     {
33051         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33052         
33053         this.iconEl.on('click', this.onClick, this);
33054     },
33055     
33056     onClick : function(e)
33057     {
33058         e.preventDefault();
33059         
33060         if(this.disabled){
33061             return;
33062         }
33063         
33064         if(this.fireEvent('click', this, e) === false){
33065             return;
33066         };
33067         
33068         this.parent().setActiveItem(this);
33069     },
33070     
33071     isActive: function () 
33072     {
33073         return this.active;
33074     },
33075     
33076     setActive : function(state)
33077     {
33078         if(this.active == state){
33079             return;
33080         }
33081         
33082         this.active = state;
33083         
33084         if (state) {
33085             this.el.addClass('active');
33086             return;
33087         }
33088         
33089         this.el.removeClass('active');
33090         
33091         return;
33092     },
33093     
33094     setDisabled : function(state)
33095     {
33096         if(this.disabled == state){
33097             return;
33098         }
33099         
33100         this.disabled = state;
33101         
33102         if (state) {
33103             this.el.addClass('disabled');
33104             return;
33105         }
33106         
33107         this.el.removeClass('disabled');
33108     },
33109     
33110     tooltipEl : function()
33111     {
33112         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33113     }
33114 });
33115  
33116
33117  /*
33118  * - LGPL
33119  *
33120  * FieldLabel
33121  * 
33122  */
33123
33124 /**
33125  * @class Roo.bootstrap.FieldLabel
33126  * @extends Roo.bootstrap.Component
33127  * Bootstrap FieldLabel class
33128  * @cfg {String} html contents of the element
33129  * @cfg {String} tag tag of the element default label
33130  * @cfg {String} cls class of the element
33131  * @cfg {String} target label target 
33132  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33133  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33134  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33135  * @cfg {String} iconTooltip default "This field is required"
33136  * @cfg {String} indicatorpos (left|right) default left
33137  * 
33138  * @constructor
33139  * Create a new FieldLabel
33140  * @param {Object} config The config object
33141  */
33142
33143 Roo.bootstrap.FieldLabel = function(config){
33144     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33145     
33146     this.addEvents({
33147             /**
33148              * @event invalid
33149              * Fires after the field has been marked as invalid.
33150              * @param {Roo.form.FieldLabel} this
33151              * @param {String} msg The validation message
33152              */
33153             invalid : true,
33154             /**
33155              * @event valid
33156              * Fires after the field has been validated with no errors.
33157              * @param {Roo.form.FieldLabel} this
33158              */
33159             valid : true
33160         });
33161 };
33162
33163 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33164     
33165     tag: 'label',
33166     cls: '',
33167     html: '',
33168     target: '',
33169     allowBlank : true,
33170     invalidClass : 'has-warning',
33171     validClass : 'has-success',
33172     iconTooltip : 'This field is required',
33173     indicatorpos : 'left',
33174     
33175     getAutoCreate : function(){
33176         
33177         var cls = "";
33178         if (!this.allowBlank) {
33179             cls  = "visible";
33180         }
33181         
33182         var cfg = {
33183             tag : this.tag,
33184             cls : 'roo-bootstrap-field-label ' + this.cls,
33185             for : this.target,
33186             cn : [
33187                 {
33188                     tag : 'i',
33189                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33190                     tooltip : this.iconTooltip
33191                 },
33192                 {
33193                     tag : 'span',
33194                     html : this.html
33195                 }
33196             ] 
33197         };
33198         
33199         if(this.indicatorpos == 'right'){
33200             var cfg = {
33201                 tag : this.tag,
33202                 cls : 'roo-bootstrap-field-label ' + this.cls,
33203                 for : this.target,
33204                 cn : [
33205                     {
33206                         tag : 'span',
33207                         html : this.html
33208                     },
33209                     {
33210                         tag : 'i',
33211                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33212                         tooltip : this.iconTooltip
33213                     }
33214                 ] 
33215             };
33216         }
33217         
33218         return cfg;
33219     },
33220     
33221     initEvents: function() 
33222     {
33223         Roo.bootstrap.Element.superclass.initEvents.call(this);
33224         
33225         this.indicator = this.indicatorEl();
33226         
33227         if(this.indicator){
33228             this.indicator.removeClass('visible');
33229             this.indicator.addClass('invisible');
33230         }
33231         
33232         Roo.bootstrap.FieldLabel.register(this);
33233     },
33234     
33235     indicatorEl : function()
33236     {
33237         var indicator = this.el.select('i.roo-required-indicator',true).first();
33238         
33239         if(!indicator){
33240             return false;
33241         }
33242         
33243         return indicator;
33244         
33245     },
33246     
33247     /**
33248      * Mark this field as valid
33249      */
33250     markValid : function()
33251     {
33252         if(this.indicator){
33253             this.indicator.removeClass('visible');
33254             this.indicator.addClass('invisible');
33255         }
33256         if (Roo.bootstrap.version == 3) {
33257             this.el.removeClass(this.invalidClass);
33258             this.el.addClass(this.validClass);
33259         } else {
33260             this.el.removeClass('is-invalid');
33261             this.el.addClass('is-valid');
33262         }
33263         
33264         
33265         this.fireEvent('valid', this);
33266     },
33267     
33268     /**
33269      * Mark this field as invalid
33270      * @param {String} msg The validation message
33271      */
33272     markInvalid : function(msg)
33273     {
33274         if(this.indicator){
33275             this.indicator.removeClass('invisible');
33276             this.indicator.addClass('visible');
33277         }
33278           if (Roo.bootstrap.version == 3) {
33279             this.el.removeClass(this.validClass);
33280             this.el.addClass(this.invalidClass);
33281         } else {
33282             this.el.removeClass('is-valid');
33283             this.el.addClass('is-invalid');
33284         }
33285         
33286         
33287         this.fireEvent('invalid', this, msg);
33288     }
33289     
33290    
33291 });
33292
33293 Roo.apply(Roo.bootstrap.FieldLabel, {
33294     
33295     groups: {},
33296     
33297      /**
33298     * register a FieldLabel Group
33299     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33300     */
33301     register : function(label)
33302     {
33303         if(this.groups.hasOwnProperty(label.target)){
33304             return;
33305         }
33306      
33307         this.groups[label.target] = label;
33308         
33309     },
33310     /**
33311     * fetch a FieldLabel Group based on the target
33312     * @param {string} target
33313     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33314     */
33315     get: function(target) {
33316         if (typeof(this.groups[target]) == 'undefined') {
33317             return false;
33318         }
33319         
33320         return this.groups[target] ;
33321     }
33322 });
33323
33324  
33325
33326  /*
33327  * - LGPL
33328  *
33329  * page DateSplitField.
33330  * 
33331  */
33332
33333
33334 /**
33335  * @class Roo.bootstrap.DateSplitField
33336  * @extends Roo.bootstrap.Component
33337  * Bootstrap DateSplitField class
33338  * @cfg {string} fieldLabel - the label associated
33339  * @cfg {Number} labelWidth set the width of label (0-12)
33340  * @cfg {String} labelAlign (top|left)
33341  * @cfg {Boolean} dayAllowBlank (true|false) default false
33342  * @cfg {Boolean} monthAllowBlank (true|false) default false
33343  * @cfg {Boolean} yearAllowBlank (true|false) default false
33344  * @cfg {string} dayPlaceholder 
33345  * @cfg {string} monthPlaceholder
33346  * @cfg {string} yearPlaceholder
33347  * @cfg {string} dayFormat default 'd'
33348  * @cfg {string} monthFormat default 'm'
33349  * @cfg {string} yearFormat default 'Y'
33350  * @cfg {Number} labellg set the width of label (1-12)
33351  * @cfg {Number} labelmd set the width of label (1-12)
33352  * @cfg {Number} labelsm set the width of label (1-12)
33353  * @cfg {Number} labelxs set the width of label (1-12)
33354
33355  *     
33356  * @constructor
33357  * Create a new DateSplitField
33358  * @param {Object} config The config object
33359  */
33360
33361 Roo.bootstrap.DateSplitField = function(config){
33362     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33363     
33364     this.addEvents({
33365         // raw events
33366          /**
33367          * @event years
33368          * getting the data of years
33369          * @param {Roo.bootstrap.DateSplitField} this
33370          * @param {Object} years
33371          */
33372         "years" : true,
33373         /**
33374          * @event days
33375          * getting the data of days
33376          * @param {Roo.bootstrap.DateSplitField} this
33377          * @param {Object} days
33378          */
33379         "days" : true,
33380         /**
33381          * @event invalid
33382          * Fires after the field has been marked as invalid.
33383          * @param {Roo.form.Field} this
33384          * @param {String} msg The validation message
33385          */
33386         invalid : true,
33387        /**
33388          * @event valid
33389          * Fires after the field has been validated with no errors.
33390          * @param {Roo.form.Field} this
33391          */
33392         valid : true
33393     });
33394 };
33395
33396 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33397     
33398     fieldLabel : '',
33399     labelAlign : 'top',
33400     labelWidth : 3,
33401     dayAllowBlank : false,
33402     monthAllowBlank : false,
33403     yearAllowBlank : false,
33404     dayPlaceholder : '',
33405     monthPlaceholder : '',
33406     yearPlaceholder : '',
33407     dayFormat : 'd',
33408     monthFormat : 'm',
33409     yearFormat : 'Y',
33410     isFormField : true,
33411     labellg : 0,
33412     labelmd : 0,
33413     labelsm : 0,
33414     labelxs : 0,
33415     
33416     getAutoCreate : function()
33417     {
33418         var cfg = {
33419             tag : 'div',
33420             cls : 'row roo-date-split-field-group',
33421             cn : [
33422                 {
33423                     tag : 'input',
33424                     type : 'hidden',
33425                     cls : 'form-hidden-field roo-date-split-field-group-value',
33426                     name : this.name
33427                 }
33428             ]
33429         };
33430         
33431         var labelCls = 'col-md-12';
33432         var contentCls = 'col-md-4';
33433         
33434         if(this.fieldLabel){
33435             
33436             var label = {
33437                 tag : 'div',
33438                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33439                 cn : [
33440                     {
33441                         tag : 'label',
33442                         html : this.fieldLabel
33443                     }
33444                 ]
33445             };
33446             
33447             if(this.labelAlign == 'left'){
33448             
33449                 if(this.labelWidth > 12){
33450                     label.style = "width: " + this.labelWidth + 'px';
33451                 }
33452
33453                 if(this.labelWidth < 13 && this.labelmd == 0){
33454                     this.labelmd = this.labelWidth;
33455                 }
33456
33457                 if(this.labellg > 0){
33458                     labelCls = ' col-lg-' + this.labellg;
33459                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33460                 }
33461
33462                 if(this.labelmd > 0){
33463                     labelCls = ' col-md-' + this.labelmd;
33464                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33465                 }
33466
33467                 if(this.labelsm > 0){
33468                     labelCls = ' col-sm-' + this.labelsm;
33469                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33470                 }
33471
33472                 if(this.labelxs > 0){
33473                     labelCls = ' col-xs-' + this.labelxs;
33474                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33475                 }
33476             }
33477             
33478             label.cls += ' ' + labelCls;
33479             
33480             cfg.cn.push(label);
33481         }
33482         
33483         Roo.each(['day', 'month', 'year'], function(t){
33484             cfg.cn.push({
33485                 tag : 'div',
33486                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33487             });
33488         }, this);
33489         
33490         return cfg;
33491     },
33492     
33493     inputEl: function ()
33494     {
33495         return this.el.select('.roo-date-split-field-group-value', true).first();
33496     },
33497     
33498     onRender : function(ct, position) 
33499     {
33500         var _this = this;
33501         
33502         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33503         
33504         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33505         
33506         this.dayField = new Roo.bootstrap.ComboBox({
33507             allowBlank : this.dayAllowBlank,
33508             alwaysQuery : true,
33509             displayField : 'value',
33510             editable : false,
33511             fieldLabel : '',
33512             forceSelection : true,
33513             mode : 'local',
33514             placeholder : this.dayPlaceholder,
33515             selectOnFocus : true,
33516             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33517             triggerAction : 'all',
33518             typeAhead : true,
33519             valueField : 'value',
33520             store : new Roo.data.SimpleStore({
33521                 data : (function() {    
33522                     var days = [];
33523                     _this.fireEvent('days', _this, days);
33524                     return days;
33525                 })(),
33526                 fields : [ 'value' ]
33527             }),
33528             listeners : {
33529                 select : function (_self, record, index)
33530                 {
33531                     _this.setValue(_this.getValue());
33532                 }
33533             }
33534         });
33535
33536         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33537         
33538         this.monthField = new Roo.bootstrap.MonthField({
33539             after : '<i class=\"fa fa-calendar\"></i>',
33540             allowBlank : this.monthAllowBlank,
33541             placeholder : this.monthPlaceholder,
33542             readOnly : true,
33543             listeners : {
33544                 render : function (_self)
33545                 {
33546                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33547                         e.preventDefault();
33548                         _self.focus();
33549                     });
33550                 },
33551                 select : function (_self, oldvalue, newvalue)
33552                 {
33553                     _this.setValue(_this.getValue());
33554                 }
33555             }
33556         });
33557         
33558         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33559         
33560         this.yearField = new Roo.bootstrap.ComboBox({
33561             allowBlank : this.yearAllowBlank,
33562             alwaysQuery : true,
33563             displayField : 'value',
33564             editable : false,
33565             fieldLabel : '',
33566             forceSelection : true,
33567             mode : 'local',
33568             placeholder : this.yearPlaceholder,
33569             selectOnFocus : true,
33570             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33571             triggerAction : 'all',
33572             typeAhead : true,
33573             valueField : 'value',
33574             store : new Roo.data.SimpleStore({
33575                 data : (function() {
33576                     var years = [];
33577                     _this.fireEvent('years', _this, years);
33578                     return years;
33579                 })(),
33580                 fields : [ 'value' ]
33581             }),
33582             listeners : {
33583                 select : function (_self, record, index)
33584                 {
33585                     _this.setValue(_this.getValue());
33586                 }
33587             }
33588         });
33589
33590         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33591     },
33592     
33593     setValue : function(v, format)
33594     {
33595         this.inputEl.dom.value = v;
33596         
33597         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33598         
33599         var d = Date.parseDate(v, f);
33600         
33601         if(!d){
33602             this.validate();
33603             return;
33604         }
33605         
33606         this.setDay(d.format(this.dayFormat));
33607         this.setMonth(d.format(this.monthFormat));
33608         this.setYear(d.format(this.yearFormat));
33609         
33610         this.validate();
33611         
33612         return;
33613     },
33614     
33615     setDay : function(v)
33616     {
33617         this.dayField.setValue(v);
33618         this.inputEl.dom.value = this.getValue();
33619         this.validate();
33620         return;
33621     },
33622     
33623     setMonth : function(v)
33624     {
33625         this.monthField.setValue(v, true);
33626         this.inputEl.dom.value = this.getValue();
33627         this.validate();
33628         return;
33629     },
33630     
33631     setYear : function(v)
33632     {
33633         this.yearField.setValue(v);
33634         this.inputEl.dom.value = this.getValue();
33635         this.validate();
33636         return;
33637     },
33638     
33639     getDay : function()
33640     {
33641         return this.dayField.getValue();
33642     },
33643     
33644     getMonth : function()
33645     {
33646         return this.monthField.getValue();
33647     },
33648     
33649     getYear : function()
33650     {
33651         return this.yearField.getValue();
33652     },
33653     
33654     getValue : function()
33655     {
33656         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33657         
33658         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33659         
33660         return date;
33661     },
33662     
33663     reset : function()
33664     {
33665         this.setDay('');
33666         this.setMonth('');
33667         this.setYear('');
33668         this.inputEl.dom.value = '';
33669         this.validate();
33670         return;
33671     },
33672     
33673     validate : function()
33674     {
33675         var d = this.dayField.validate();
33676         var m = this.monthField.validate();
33677         var y = this.yearField.validate();
33678         
33679         var valid = true;
33680         
33681         if(
33682                 (!this.dayAllowBlank && !d) ||
33683                 (!this.monthAllowBlank && !m) ||
33684                 (!this.yearAllowBlank && !y)
33685         ){
33686             valid = false;
33687         }
33688         
33689         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33690             return valid;
33691         }
33692         
33693         if(valid){
33694             this.markValid();
33695             return valid;
33696         }
33697         
33698         this.markInvalid();
33699         
33700         return valid;
33701     },
33702     
33703     markValid : function()
33704     {
33705         
33706         var label = this.el.select('label', true).first();
33707         var icon = this.el.select('i.fa-star', true).first();
33708
33709         if(label && icon){
33710             icon.remove();
33711         }
33712         
33713         this.fireEvent('valid', this);
33714     },
33715     
33716      /**
33717      * Mark this field as invalid
33718      * @param {String} msg The validation message
33719      */
33720     markInvalid : function(msg)
33721     {
33722         
33723         var label = this.el.select('label', true).first();
33724         var icon = this.el.select('i.fa-star', true).first();
33725
33726         if(label && !icon){
33727             this.el.select('.roo-date-split-field-label', true).createChild({
33728                 tag : 'i',
33729                 cls : 'text-danger fa fa-lg fa-star',
33730                 tooltip : 'This field is required',
33731                 style : 'margin-right:5px;'
33732             }, label, true);
33733         }
33734         
33735         this.fireEvent('invalid', this, msg);
33736     },
33737     
33738     clearInvalid : function()
33739     {
33740         var label = this.el.select('label', true).first();
33741         var icon = this.el.select('i.fa-star', true).first();
33742
33743         if(label && icon){
33744             icon.remove();
33745         }
33746         
33747         this.fireEvent('valid', this);
33748     },
33749     
33750     getName: function()
33751     {
33752         return this.name;
33753     }
33754     
33755 });
33756
33757  /**
33758  *
33759  * This is based on 
33760  * http://masonry.desandro.com
33761  *
33762  * The idea is to render all the bricks based on vertical width...
33763  *
33764  * The original code extends 'outlayer' - we might need to use that....
33765  * 
33766  */
33767
33768
33769 /**
33770  * @class Roo.bootstrap.LayoutMasonry
33771  * @extends Roo.bootstrap.Component
33772  * Bootstrap Layout Masonry class
33773  * 
33774  * @constructor
33775  * Create a new Element
33776  * @param {Object} config The config object
33777  */
33778
33779 Roo.bootstrap.LayoutMasonry = function(config){
33780     
33781     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33782     
33783     this.bricks = [];
33784     
33785     Roo.bootstrap.LayoutMasonry.register(this);
33786     
33787     this.addEvents({
33788         // raw events
33789         /**
33790          * @event layout
33791          * Fire after layout the items
33792          * @param {Roo.bootstrap.LayoutMasonry} this
33793          * @param {Roo.EventObject} e
33794          */
33795         "layout" : true
33796     });
33797     
33798 };
33799
33800 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33801     
33802     /**
33803      * @cfg {Boolean} isLayoutInstant = no animation?
33804      */   
33805     isLayoutInstant : false, // needed?
33806    
33807     /**
33808      * @cfg {Number} boxWidth  width of the columns
33809      */   
33810     boxWidth : 450,
33811     
33812       /**
33813      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33814      */   
33815     boxHeight : 0,
33816     
33817     /**
33818      * @cfg {Number} padWidth padding below box..
33819      */   
33820     padWidth : 10, 
33821     
33822     /**
33823      * @cfg {Number} gutter gutter width..
33824      */   
33825     gutter : 10,
33826     
33827      /**
33828      * @cfg {Number} maxCols maximum number of columns
33829      */   
33830     
33831     maxCols: 0,
33832     
33833     /**
33834      * @cfg {Boolean} isAutoInitial defalut true
33835      */   
33836     isAutoInitial : true, 
33837     
33838     containerWidth: 0,
33839     
33840     /**
33841      * @cfg {Boolean} isHorizontal defalut false
33842      */   
33843     isHorizontal : false, 
33844
33845     currentSize : null,
33846     
33847     tag: 'div',
33848     
33849     cls: '',
33850     
33851     bricks: null, //CompositeElement
33852     
33853     cols : 1,
33854     
33855     _isLayoutInited : false,
33856     
33857 //    isAlternative : false, // only use for vertical layout...
33858     
33859     /**
33860      * @cfg {Number} alternativePadWidth padding below box..
33861      */   
33862     alternativePadWidth : 50,
33863     
33864     selectedBrick : [],
33865     
33866     getAutoCreate : function(){
33867         
33868         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33869         
33870         var cfg = {
33871             tag: this.tag,
33872             cls: 'blog-masonary-wrapper ' + this.cls,
33873             cn : {
33874                 cls : 'mas-boxes masonary'
33875             }
33876         };
33877         
33878         return cfg;
33879     },
33880     
33881     getChildContainer: function( )
33882     {
33883         if (this.boxesEl) {
33884             return this.boxesEl;
33885         }
33886         
33887         this.boxesEl = this.el.select('.mas-boxes').first();
33888         
33889         return this.boxesEl;
33890     },
33891     
33892     
33893     initEvents : function()
33894     {
33895         var _this = this;
33896         
33897         if(this.isAutoInitial){
33898             Roo.log('hook children rendered');
33899             this.on('childrenrendered', function() {
33900                 Roo.log('children rendered');
33901                 _this.initial();
33902             } ,this);
33903         }
33904     },
33905     
33906     initial : function()
33907     {
33908         this.selectedBrick = [];
33909         
33910         this.currentSize = this.el.getBox(true);
33911         
33912         Roo.EventManager.onWindowResize(this.resize, this); 
33913
33914         if(!this.isAutoInitial){
33915             this.layout();
33916             return;
33917         }
33918         
33919         this.layout();
33920         
33921         return;
33922         //this.layout.defer(500,this);
33923         
33924     },
33925     
33926     resize : function()
33927     {
33928         var cs = this.el.getBox(true);
33929         
33930         if (
33931                 this.currentSize.width == cs.width && 
33932                 this.currentSize.x == cs.x && 
33933                 this.currentSize.height == cs.height && 
33934                 this.currentSize.y == cs.y 
33935         ) {
33936             Roo.log("no change in with or X or Y");
33937             return;
33938         }
33939         
33940         this.currentSize = cs;
33941         
33942         this.layout();
33943         
33944     },
33945     
33946     layout : function()
33947     {   
33948         this._resetLayout();
33949         
33950         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33951         
33952         this.layoutItems( isInstant );
33953       
33954         this._isLayoutInited = true;
33955         
33956         this.fireEvent('layout', this);
33957         
33958     },
33959     
33960     _resetLayout : function()
33961     {
33962         if(this.isHorizontal){
33963             this.horizontalMeasureColumns();
33964             return;
33965         }
33966         
33967         this.verticalMeasureColumns();
33968         
33969     },
33970     
33971     verticalMeasureColumns : function()
33972     {
33973         this.getContainerWidth();
33974         
33975 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33976 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33977 //            return;
33978 //        }
33979         
33980         var boxWidth = this.boxWidth + this.padWidth;
33981         
33982         if(this.containerWidth < this.boxWidth){
33983             boxWidth = this.containerWidth
33984         }
33985         
33986         var containerWidth = this.containerWidth;
33987         
33988         var cols = Math.floor(containerWidth / boxWidth);
33989         
33990         this.cols = Math.max( cols, 1 );
33991         
33992         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33993         
33994         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33995         
33996         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33997         
33998         this.colWidth = boxWidth + avail - this.padWidth;
33999         
34000         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34001         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34002     },
34003     
34004     horizontalMeasureColumns : function()
34005     {
34006         this.getContainerWidth();
34007         
34008         var boxWidth = this.boxWidth;
34009         
34010         if(this.containerWidth < boxWidth){
34011             boxWidth = this.containerWidth;
34012         }
34013         
34014         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34015         
34016         this.el.setHeight(boxWidth);
34017         
34018     },
34019     
34020     getContainerWidth : function()
34021     {
34022         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34023     },
34024     
34025     layoutItems : function( isInstant )
34026     {
34027         Roo.log(this.bricks);
34028         
34029         var items = Roo.apply([], this.bricks);
34030         
34031         if(this.isHorizontal){
34032             this._horizontalLayoutItems( items , isInstant );
34033             return;
34034         }
34035         
34036 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34037 //            this._verticalAlternativeLayoutItems( items , isInstant );
34038 //            return;
34039 //        }
34040         
34041         this._verticalLayoutItems( items , isInstant );
34042         
34043     },
34044     
34045     _verticalLayoutItems : function ( items , isInstant)
34046     {
34047         if ( !items || !items.length ) {
34048             return;
34049         }
34050         
34051         var standard = [
34052             ['xs', 'xs', 'xs', 'tall'],
34053             ['xs', 'xs', 'tall'],
34054             ['xs', 'xs', 'sm'],
34055             ['xs', 'xs', 'xs'],
34056             ['xs', 'tall'],
34057             ['xs', 'sm'],
34058             ['xs', 'xs'],
34059             ['xs'],
34060             
34061             ['sm', 'xs', 'xs'],
34062             ['sm', 'xs'],
34063             ['sm'],
34064             
34065             ['tall', 'xs', 'xs', 'xs'],
34066             ['tall', 'xs', 'xs'],
34067             ['tall', 'xs'],
34068             ['tall']
34069             
34070         ];
34071         
34072         var queue = [];
34073         
34074         var boxes = [];
34075         
34076         var box = [];
34077         
34078         Roo.each(items, function(item, k){
34079             
34080             switch (item.size) {
34081                 // these layouts take up a full box,
34082                 case 'md' :
34083                 case 'md-left' :
34084                 case 'md-right' :
34085                 case 'wide' :
34086                     
34087                     if(box.length){
34088                         boxes.push(box);
34089                         box = [];
34090                     }
34091                     
34092                     boxes.push([item]);
34093                     
34094                     break;
34095                     
34096                 case 'xs' :
34097                 case 'sm' :
34098                 case 'tall' :
34099                     
34100                     box.push(item);
34101                     
34102                     break;
34103                 default :
34104                     break;
34105                     
34106             }
34107             
34108         }, this);
34109         
34110         if(box.length){
34111             boxes.push(box);
34112             box = [];
34113         }
34114         
34115         var filterPattern = function(box, length)
34116         {
34117             if(!box.length){
34118                 return;
34119             }
34120             
34121             var match = false;
34122             
34123             var pattern = box.slice(0, length);
34124             
34125             var format = [];
34126             
34127             Roo.each(pattern, function(i){
34128                 format.push(i.size);
34129             }, this);
34130             
34131             Roo.each(standard, function(s){
34132                 
34133                 if(String(s) != String(format)){
34134                     return;
34135                 }
34136                 
34137                 match = true;
34138                 return false;
34139                 
34140             }, this);
34141             
34142             if(!match && length == 1){
34143                 return;
34144             }
34145             
34146             if(!match){
34147                 filterPattern(box, length - 1);
34148                 return;
34149             }
34150                 
34151             queue.push(pattern);
34152
34153             box = box.slice(length, box.length);
34154
34155             filterPattern(box, 4);
34156
34157             return;
34158             
34159         }
34160         
34161         Roo.each(boxes, function(box, k){
34162             
34163             if(!box.length){
34164                 return;
34165             }
34166             
34167             if(box.length == 1){
34168                 queue.push(box);
34169                 return;
34170             }
34171             
34172             filterPattern(box, 4);
34173             
34174         }, this);
34175         
34176         this._processVerticalLayoutQueue( queue, isInstant );
34177         
34178     },
34179     
34180 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34181 //    {
34182 //        if ( !items || !items.length ) {
34183 //            return;
34184 //        }
34185 //
34186 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34187 //        
34188 //    },
34189     
34190     _horizontalLayoutItems : function ( items , isInstant)
34191     {
34192         if ( !items || !items.length || items.length < 3) {
34193             return;
34194         }
34195         
34196         items.reverse();
34197         
34198         var eItems = items.slice(0, 3);
34199         
34200         items = items.slice(3, items.length);
34201         
34202         var standard = [
34203             ['xs', 'xs', 'xs', 'wide'],
34204             ['xs', 'xs', 'wide'],
34205             ['xs', 'xs', 'sm'],
34206             ['xs', 'xs', 'xs'],
34207             ['xs', 'wide'],
34208             ['xs', 'sm'],
34209             ['xs', 'xs'],
34210             ['xs'],
34211             
34212             ['sm', 'xs', 'xs'],
34213             ['sm', 'xs'],
34214             ['sm'],
34215             
34216             ['wide', 'xs', 'xs', 'xs'],
34217             ['wide', 'xs', 'xs'],
34218             ['wide', 'xs'],
34219             ['wide'],
34220             
34221             ['wide-thin']
34222         ];
34223         
34224         var queue = [];
34225         
34226         var boxes = [];
34227         
34228         var box = [];
34229         
34230         Roo.each(items, function(item, k){
34231             
34232             switch (item.size) {
34233                 case 'md' :
34234                 case 'md-left' :
34235                 case 'md-right' :
34236                 case 'tall' :
34237                     
34238                     if(box.length){
34239                         boxes.push(box);
34240                         box = [];
34241                     }
34242                     
34243                     boxes.push([item]);
34244                     
34245                     break;
34246                     
34247                 case 'xs' :
34248                 case 'sm' :
34249                 case 'wide' :
34250                 case 'wide-thin' :
34251                     
34252                     box.push(item);
34253                     
34254                     break;
34255                 default :
34256                     break;
34257                     
34258             }
34259             
34260         }, this);
34261         
34262         if(box.length){
34263             boxes.push(box);
34264             box = [];
34265         }
34266         
34267         var filterPattern = function(box, length)
34268         {
34269             if(!box.length){
34270                 return;
34271             }
34272             
34273             var match = false;
34274             
34275             var pattern = box.slice(0, length);
34276             
34277             var format = [];
34278             
34279             Roo.each(pattern, function(i){
34280                 format.push(i.size);
34281             }, this);
34282             
34283             Roo.each(standard, function(s){
34284                 
34285                 if(String(s) != String(format)){
34286                     return;
34287                 }
34288                 
34289                 match = true;
34290                 return false;
34291                 
34292             }, this);
34293             
34294             if(!match && length == 1){
34295                 return;
34296             }
34297             
34298             if(!match){
34299                 filterPattern(box, length - 1);
34300                 return;
34301             }
34302                 
34303             queue.push(pattern);
34304
34305             box = box.slice(length, box.length);
34306
34307             filterPattern(box, 4);
34308
34309             return;
34310             
34311         }
34312         
34313         Roo.each(boxes, function(box, k){
34314             
34315             if(!box.length){
34316                 return;
34317             }
34318             
34319             if(box.length == 1){
34320                 queue.push(box);
34321                 return;
34322             }
34323             
34324             filterPattern(box, 4);
34325             
34326         }, this);
34327         
34328         
34329         var prune = [];
34330         
34331         var pos = this.el.getBox(true);
34332         
34333         var minX = pos.x;
34334         
34335         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34336         
34337         var hit_end = false;
34338         
34339         Roo.each(queue, function(box){
34340             
34341             if(hit_end){
34342                 
34343                 Roo.each(box, function(b){
34344                 
34345                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34346                     b.el.hide();
34347
34348                 }, this);
34349
34350                 return;
34351             }
34352             
34353             var mx = 0;
34354             
34355             Roo.each(box, function(b){
34356                 
34357                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34358                 b.el.show();
34359
34360                 mx = Math.max(mx, b.x);
34361                 
34362             }, this);
34363             
34364             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34365             
34366             if(maxX < minX){
34367                 
34368                 Roo.each(box, function(b){
34369                 
34370                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34371                     b.el.hide();
34372                     
34373                 }, this);
34374                 
34375                 hit_end = true;
34376                 
34377                 return;
34378             }
34379             
34380             prune.push(box);
34381             
34382         }, this);
34383         
34384         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34385     },
34386     
34387     /** Sets position of item in DOM
34388     * @param {Element} item
34389     * @param {Number} x - horizontal position
34390     * @param {Number} y - vertical position
34391     * @param {Boolean} isInstant - disables transitions
34392     */
34393     _processVerticalLayoutQueue : function( queue, isInstant )
34394     {
34395         var pos = this.el.getBox(true);
34396         var x = pos.x;
34397         var y = pos.y;
34398         var maxY = [];
34399         
34400         for (var i = 0; i < this.cols; i++){
34401             maxY[i] = pos.y;
34402         }
34403         
34404         Roo.each(queue, function(box, k){
34405             
34406             var col = k % this.cols;
34407             
34408             Roo.each(box, function(b,kk){
34409                 
34410                 b.el.position('absolute');
34411                 
34412                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34413                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34414                 
34415                 if(b.size == 'md-left' || b.size == 'md-right'){
34416                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34417                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34418                 }
34419                 
34420                 b.el.setWidth(width);
34421                 b.el.setHeight(height);
34422                 // iframe?
34423                 b.el.select('iframe',true).setSize(width,height);
34424                 
34425             }, this);
34426             
34427             for (var i = 0; i < this.cols; i++){
34428                 
34429                 if(maxY[i] < maxY[col]){
34430                     col = i;
34431                     continue;
34432                 }
34433                 
34434                 col = Math.min(col, i);
34435                 
34436             }
34437             
34438             x = pos.x + col * (this.colWidth + this.padWidth);
34439             
34440             y = maxY[col];
34441             
34442             var positions = [];
34443             
34444             switch (box.length){
34445                 case 1 :
34446                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34447                     break;
34448                 case 2 :
34449                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34450                     break;
34451                 case 3 :
34452                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34453                     break;
34454                 case 4 :
34455                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34456                     break;
34457                 default :
34458                     break;
34459             }
34460             
34461             Roo.each(box, function(b,kk){
34462                 
34463                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34464                 
34465                 var sz = b.el.getSize();
34466                 
34467                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34468                 
34469             }, this);
34470             
34471         }, this);
34472         
34473         var mY = 0;
34474         
34475         for (var i = 0; i < this.cols; i++){
34476             mY = Math.max(mY, maxY[i]);
34477         }
34478         
34479         this.el.setHeight(mY - pos.y);
34480         
34481     },
34482     
34483 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34484 //    {
34485 //        var pos = this.el.getBox(true);
34486 //        var x = pos.x;
34487 //        var y = pos.y;
34488 //        var maxX = pos.right;
34489 //        
34490 //        var maxHeight = 0;
34491 //        
34492 //        Roo.each(items, function(item, k){
34493 //            
34494 //            var c = k % 2;
34495 //            
34496 //            item.el.position('absolute');
34497 //                
34498 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34499 //
34500 //            item.el.setWidth(width);
34501 //
34502 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34503 //
34504 //            item.el.setHeight(height);
34505 //            
34506 //            if(c == 0){
34507 //                item.el.setXY([x, y], isInstant ? false : true);
34508 //            } else {
34509 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34510 //            }
34511 //            
34512 //            y = y + height + this.alternativePadWidth;
34513 //            
34514 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34515 //            
34516 //        }, this);
34517 //        
34518 //        this.el.setHeight(maxHeight);
34519 //        
34520 //    },
34521     
34522     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34523     {
34524         var pos = this.el.getBox(true);
34525         
34526         var minX = pos.x;
34527         var minY = pos.y;
34528         
34529         var maxX = pos.right;
34530         
34531         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34532         
34533         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34534         
34535         Roo.each(queue, function(box, k){
34536             
34537             Roo.each(box, function(b, kk){
34538                 
34539                 b.el.position('absolute');
34540                 
34541                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34542                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34543                 
34544                 if(b.size == 'md-left' || b.size == 'md-right'){
34545                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34546                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34547                 }
34548                 
34549                 b.el.setWidth(width);
34550                 b.el.setHeight(height);
34551                 
34552             }, this);
34553             
34554             if(!box.length){
34555                 return;
34556             }
34557             
34558             var positions = [];
34559             
34560             switch (box.length){
34561                 case 1 :
34562                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34563                     break;
34564                 case 2 :
34565                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34566                     break;
34567                 case 3 :
34568                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34569                     break;
34570                 case 4 :
34571                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34572                     break;
34573                 default :
34574                     break;
34575             }
34576             
34577             Roo.each(box, function(b,kk){
34578                 
34579                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34580                 
34581                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34582                 
34583             }, this);
34584             
34585         }, this);
34586         
34587     },
34588     
34589     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34590     {
34591         Roo.each(eItems, function(b,k){
34592             
34593             b.size = (k == 0) ? 'sm' : 'xs';
34594             b.x = (k == 0) ? 2 : 1;
34595             b.y = (k == 0) ? 2 : 1;
34596             
34597             b.el.position('absolute');
34598             
34599             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34600                 
34601             b.el.setWidth(width);
34602             
34603             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34604             
34605             b.el.setHeight(height);
34606             
34607         }, this);
34608
34609         var positions = [];
34610         
34611         positions.push({
34612             x : maxX - this.unitWidth * 2 - this.gutter,
34613             y : minY
34614         });
34615         
34616         positions.push({
34617             x : maxX - this.unitWidth,
34618             y : minY + (this.unitWidth + this.gutter) * 2
34619         });
34620         
34621         positions.push({
34622             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34623             y : minY
34624         });
34625         
34626         Roo.each(eItems, function(b,k){
34627             
34628             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34629
34630         }, this);
34631         
34632     },
34633     
34634     getVerticalOneBoxColPositions : function(x, y, box)
34635     {
34636         var pos = [];
34637         
34638         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34639         
34640         if(box[0].size == 'md-left'){
34641             rand = 0;
34642         }
34643         
34644         if(box[0].size == 'md-right'){
34645             rand = 1;
34646         }
34647         
34648         pos.push({
34649             x : x + (this.unitWidth + this.gutter) * rand,
34650             y : y
34651         });
34652         
34653         return pos;
34654     },
34655     
34656     getVerticalTwoBoxColPositions : function(x, y, box)
34657     {
34658         var pos = [];
34659         
34660         if(box[0].size == 'xs'){
34661             
34662             pos.push({
34663                 x : x,
34664                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34665             });
34666
34667             pos.push({
34668                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34669                 y : y
34670             });
34671             
34672             return pos;
34673             
34674         }
34675         
34676         pos.push({
34677             x : x,
34678             y : y
34679         });
34680
34681         pos.push({
34682             x : x + (this.unitWidth + this.gutter) * 2,
34683             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34684         });
34685         
34686         return pos;
34687         
34688     },
34689     
34690     getVerticalThreeBoxColPositions : function(x, y, box)
34691     {
34692         var pos = [];
34693         
34694         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34695             
34696             pos.push({
34697                 x : x,
34698                 y : y
34699             });
34700
34701             pos.push({
34702                 x : x + (this.unitWidth + this.gutter) * 1,
34703                 y : y
34704             });
34705             
34706             pos.push({
34707                 x : x + (this.unitWidth + this.gutter) * 2,
34708                 y : y
34709             });
34710             
34711             return pos;
34712             
34713         }
34714         
34715         if(box[0].size == 'xs' && box[1].size == 'xs'){
34716             
34717             pos.push({
34718                 x : x,
34719                 y : y
34720             });
34721
34722             pos.push({
34723                 x : x,
34724                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34725             });
34726             
34727             pos.push({
34728                 x : x + (this.unitWidth + this.gutter) * 1,
34729                 y : y
34730             });
34731             
34732             return pos;
34733             
34734         }
34735         
34736         pos.push({
34737             x : x,
34738             y : y
34739         });
34740
34741         pos.push({
34742             x : x + (this.unitWidth + this.gutter) * 2,
34743             y : y
34744         });
34745
34746         pos.push({
34747             x : x + (this.unitWidth + this.gutter) * 2,
34748             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34749         });
34750             
34751         return pos;
34752         
34753     },
34754     
34755     getVerticalFourBoxColPositions : function(x, y, box)
34756     {
34757         var pos = [];
34758         
34759         if(box[0].size == 'xs'){
34760             
34761             pos.push({
34762                 x : x,
34763                 y : y
34764             });
34765
34766             pos.push({
34767                 x : x,
34768                 y : y + (this.unitHeight + this.gutter) * 1
34769             });
34770             
34771             pos.push({
34772                 x : x,
34773                 y : y + (this.unitHeight + this.gutter) * 2
34774             });
34775             
34776             pos.push({
34777                 x : x + (this.unitWidth + this.gutter) * 1,
34778                 y : y
34779             });
34780             
34781             return pos;
34782             
34783         }
34784         
34785         pos.push({
34786             x : x,
34787             y : y
34788         });
34789
34790         pos.push({
34791             x : x + (this.unitWidth + this.gutter) * 2,
34792             y : y
34793         });
34794
34795         pos.push({
34796             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34797             y : y + (this.unitHeight + this.gutter) * 1
34798         });
34799
34800         pos.push({
34801             x : x + (this.unitWidth + this.gutter) * 2,
34802             y : y + (this.unitWidth + this.gutter) * 2
34803         });
34804
34805         return pos;
34806         
34807     },
34808     
34809     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34810     {
34811         var pos = [];
34812         
34813         if(box[0].size == 'md-left'){
34814             pos.push({
34815                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34816                 y : minY
34817             });
34818             
34819             return pos;
34820         }
34821         
34822         if(box[0].size == 'md-right'){
34823             pos.push({
34824                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34825                 y : minY + (this.unitWidth + this.gutter) * 1
34826             });
34827             
34828             return pos;
34829         }
34830         
34831         var rand = Math.floor(Math.random() * (4 - box[0].y));
34832         
34833         pos.push({
34834             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34835             y : minY + (this.unitWidth + this.gutter) * rand
34836         });
34837         
34838         return pos;
34839         
34840     },
34841     
34842     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34843     {
34844         var pos = [];
34845         
34846         if(box[0].size == 'xs'){
34847             
34848             pos.push({
34849                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34850                 y : minY
34851             });
34852
34853             pos.push({
34854                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34855                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34856             });
34857             
34858             return pos;
34859             
34860         }
34861         
34862         pos.push({
34863             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34864             y : minY
34865         });
34866
34867         pos.push({
34868             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34869             y : minY + (this.unitWidth + this.gutter) * 2
34870         });
34871         
34872         return pos;
34873         
34874     },
34875     
34876     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34877     {
34878         var pos = [];
34879         
34880         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34881             
34882             pos.push({
34883                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34884                 y : minY
34885             });
34886
34887             pos.push({
34888                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34889                 y : minY + (this.unitWidth + this.gutter) * 1
34890             });
34891             
34892             pos.push({
34893                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34894                 y : minY + (this.unitWidth + this.gutter) * 2
34895             });
34896             
34897             return pos;
34898             
34899         }
34900         
34901         if(box[0].size == 'xs' && box[1].size == 'xs'){
34902             
34903             pos.push({
34904                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34905                 y : minY
34906             });
34907
34908             pos.push({
34909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34910                 y : minY
34911             });
34912             
34913             pos.push({
34914                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34915                 y : minY + (this.unitWidth + this.gutter) * 1
34916             });
34917             
34918             return pos;
34919             
34920         }
34921         
34922         pos.push({
34923             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34924             y : minY
34925         });
34926
34927         pos.push({
34928             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34929             y : minY + (this.unitWidth + this.gutter) * 2
34930         });
34931
34932         pos.push({
34933             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34934             y : minY + (this.unitWidth + this.gutter) * 2
34935         });
34936             
34937         return pos;
34938         
34939     },
34940     
34941     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34942     {
34943         var pos = [];
34944         
34945         if(box[0].size == 'xs'){
34946             
34947             pos.push({
34948                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34949                 y : minY
34950             });
34951
34952             pos.push({
34953                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34954                 y : minY
34955             });
34956             
34957             pos.push({
34958                 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),
34959                 y : minY
34960             });
34961             
34962             pos.push({
34963                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34964                 y : minY + (this.unitWidth + this.gutter) * 1
34965             });
34966             
34967             return pos;
34968             
34969         }
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[1].x - this.gutter * (box[1].x - 1),
34978             y : minY + (this.unitWidth + this.gutter) * 2
34979         });
34980         
34981         pos.push({
34982             x : maxX - 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 + (this.unitWidth + this.gutter) * 2
34984         });
34985         
34986         pos.push({
34987             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),
34988             y : minY + (this.unitWidth + this.gutter) * 2
34989         });
34990
34991         return pos;
34992         
34993     },
34994     
34995     /**
34996     * remove a Masonry Brick
34997     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34998     */
34999     removeBrick : function(brick_id)
35000     {
35001         if (!brick_id) {
35002             return;
35003         }
35004         
35005         for (var i = 0; i<this.bricks.length; i++) {
35006             if (this.bricks[i].id == brick_id) {
35007                 this.bricks.splice(i,1);
35008                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35009                 this.initial();
35010             }
35011         }
35012     },
35013     
35014     /**
35015     * adds a Masonry Brick
35016     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35017     */
35018     addBrick : function(cfg)
35019     {
35020         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35021         //this.register(cn);
35022         cn.parentId = this.id;
35023         cn.render(this.el);
35024         return cn;
35025     },
35026     
35027     /**
35028     * register a Masonry Brick
35029     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35030     */
35031     
35032     register : function(brick)
35033     {
35034         this.bricks.push(brick);
35035         brick.masonryId = this.id;
35036     },
35037     
35038     /**
35039     * clear all the Masonry Brick
35040     */
35041     clearAll : function()
35042     {
35043         this.bricks = [];
35044         //this.getChildContainer().dom.innerHTML = "";
35045         this.el.dom.innerHTML = '';
35046     },
35047     
35048     getSelected : function()
35049     {
35050         if (!this.selectedBrick) {
35051             return false;
35052         }
35053         
35054         return this.selectedBrick;
35055     }
35056 });
35057
35058 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35059     
35060     groups: {},
35061      /**
35062     * register a Masonry Layout
35063     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35064     */
35065     
35066     register : function(layout)
35067     {
35068         this.groups[layout.id] = layout;
35069     },
35070     /**
35071     * fetch a  Masonry Layout based on the masonry layout ID
35072     * @param {string} the masonry layout to add
35073     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35074     */
35075     
35076     get: function(layout_id) {
35077         if (typeof(this.groups[layout_id]) == 'undefined') {
35078             return false;
35079         }
35080         return this.groups[layout_id] ;
35081     }
35082     
35083     
35084     
35085 });
35086
35087  
35088
35089  /**
35090  *
35091  * This is based on 
35092  * http://masonry.desandro.com
35093  *
35094  * The idea is to render all the bricks based on vertical width...
35095  *
35096  * The original code extends 'outlayer' - we might need to use that....
35097  * 
35098  */
35099
35100
35101 /**
35102  * @class Roo.bootstrap.LayoutMasonryAuto
35103  * @extends Roo.bootstrap.Component
35104  * Bootstrap Layout Masonry class
35105  * 
35106  * @constructor
35107  * Create a new Element
35108  * @param {Object} config The config object
35109  */
35110
35111 Roo.bootstrap.LayoutMasonryAuto = function(config){
35112     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35113 };
35114
35115 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35116     
35117       /**
35118      * @cfg {Boolean} isFitWidth  - resize the width..
35119      */   
35120     isFitWidth : false,  // options..
35121     /**
35122      * @cfg {Boolean} isOriginLeft = left align?
35123      */   
35124     isOriginLeft : true,
35125     /**
35126      * @cfg {Boolean} isOriginTop = top align?
35127      */   
35128     isOriginTop : false,
35129     /**
35130      * @cfg {Boolean} isLayoutInstant = no animation?
35131      */   
35132     isLayoutInstant : false, // needed?
35133     /**
35134      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35135      */   
35136     isResizingContainer : true,
35137     /**
35138      * @cfg {Number} columnWidth  width of the columns 
35139      */   
35140     
35141     columnWidth : 0,
35142     
35143     /**
35144      * @cfg {Number} maxCols maximum number of columns
35145      */   
35146     
35147     maxCols: 0,
35148     /**
35149      * @cfg {Number} padHeight padding below box..
35150      */   
35151     
35152     padHeight : 10, 
35153     
35154     /**
35155      * @cfg {Boolean} isAutoInitial defalut true
35156      */   
35157     
35158     isAutoInitial : true, 
35159     
35160     // private?
35161     gutter : 0,
35162     
35163     containerWidth: 0,
35164     initialColumnWidth : 0,
35165     currentSize : null,
35166     
35167     colYs : null, // array.
35168     maxY : 0,
35169     padWidth: 10,
35170     
35171     
35172     tag: 'div',
35173     cls: '',
35174     bricks: null, //CompositeElement
35175     cols : 0, // array?
35176     // element : null, // wrapped now this.el
35177     _isLayoutInited : null, 
35178     
35179     
35180     getAutoCreate : function(){
35181         
35182         var cfg = {
35183             tag: this.tag,
35184             cls: 'blog-masonary-wrapper ' + this.cls,
35185             cn : {
35186                 cls : 'mas-boxes masonary'
35187             }
35188         };
35189         
35190         return cfg;
35191     },
35192     
35193     getChildContainer: function( )
35194     {
35195         if (this.boxesEl) {
35196             return this.boxesEl;
35197         }
35198         
35199         this.boxesEl = this.el.select('.mas-boxes').first();
35200         
35201         return this.boxesEl;
35202     },
35203     
35204     
35205     initEvents : function()
35206     {
35207         var _this = this;
35208         
35209         if(this.isAutoInitial){
35210             Roo.log('hook children rendered');
35211             this.on('childrenrendered', function() {
35212                 Roo.log('children rendered');
35213                 _this.initial();
35214             } ,this);
35215         }
35216         
35217     },
35218     
35219     initial : function()
35220     {
35221         this.reloadItems();
35222
35223         this.currentSize = this.el.getBox(true);
35224
35225         /// was window resize... - let's see if this works..
35226         Roo.EventManager.onWindowResize(this.resize, this); 
35227
35228         if(!this.isAutoInitial){
35229             this.layout();
35230             return;
35231         }
35232         
35233         this.layout.defer(500,this);
35234     },
35235     
35236     reloadItems: function()
35237     {
35238         this.bricks = this.el.select('.masonry-brick', true);
35239         
35240         this.bricks.each(function(b) {
35241             //Roo.log(b.getSize());
35242             if (!b.attr('originalwidth')) {
35243                 b.attr('originalwidth',  b.getSize().width);
35244             }
35245             
35246         });
35247         
35248         Roo.log(this.bricks.elements.length);
35249     },
35250     
35251     resize : function()
35252     {
35253         Roo.log('resize');
35254         var cs = this.el.getBox(true);
35255         
35256         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35257             Roo.log("no change in with or X");
35258             return;
35259         }
35260         this.currentSize = cs;
35261         this.layout();
35262     },
35263     
35264     layout : function()
35265     {
35266          Roo.log('layout');
35267         this._resetLayout();
35268         //this._manageStamps();
35269       
35270         // don't animate first layout
35271         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35272         this.layoutItems( isInstant );
35273       
35274         // flag for initalized
35275         this._isLayoutInited = true;
35276     },
35277     
35278     layoutItems : function( isInstant )
35279     {
35280         //var items = this._getItemsForLayout( this.items );
35281         // original code supports filtering layout items.. we just ignore it..
35282         
35283         this._layoutItems( this.bricks , isInstant );
35284       
35285         this._postLayout();
35286     },
35287     _layoutItems : function ( items , isInstant)
35288     {
35289        //this.fireEvent( 'layout', this, items );
35290     
35291
35292         if ( !items || !items.elements.length ) {
35293           // no items, emit event with empty array
35294             return;
35295         }
35296
35297         var queue = [];
35298         items.each(function(item) {
35299             Roo.log("layout item");
35300             Roo.log(item);
35301             // get x/y object from method
35302             var position = this._getItemLayoutPosition( item );
35303             // enqueue
35304             position.item = item;
35305             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35306             queue.push( position );
35307         }, this);
35308       
35309         this._processLayoutQueue( queue );
35310     },
35311     /** Sets position of item in DOM
35312     * @param {Element} item
35313     * @param {Number} x - horizontal position
35314     * @param {Number} y - vertical position
35315     * @param {Boolean} isInstant - disables transitions
35316     */
35317     _processLayoutQueue : function( queue )
35318     {
35319         for ( var i=0, len = queue.length; i < len; i++ ) {
35320             var obj = queue[i];
35321             obj.item.position('absolute');
35322             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35323         }
35324     },
35325       
35326     
35327     /**
35328     * Any logic you want to do after each layout,
35329     * i.e. size the container
35330     */
35331     _postLayout : function()
35332     {
35333         this.resizeContainer();
35334     },
35335     
35336     resizeContainer : function()
35337     {
35338         if ( !this.isResizingContainer ) {
35339             return;
35340         }
35341         var size = this._getContainerSize();
35342         if ( size ) {
35343             this.el.setSize(size.width,size.height);
35344             this.boxesEl.setSize(size.width,size.height);
35345         }
35346     },
35347     
35348     
35349     
35350     _resetLayout : function()
35351     {
35352         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35353         this.colWidth = this.el.getWidth();
35354         //this.gutter = this.el.getWidth(); 
35355         
35356         this.measureColumns();
35357
35358         // reset column Y
35359         var i = this.cols;
35360         this.colYs = [];
35361         while (i--) {
35362             this.colYs.push( 0 );
35363         }
35364     
35365         this.maxY = 0;
35366     },
35367
35368     measureColumns : function()
35369     {
35370         this.getContainerWidth();
35371       // if columnWidth is 0, default to outerWidth of first item
35372         if ( !this.columnWidth ) {
35373             var firstItem = this.bricks.first();
35374             Roo.log(firstItem);
35375             this.columnWidth  = this.containerWidth;
35376             if (firstItem && firstItem.attr('originalwidth') ) {
35377                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35378             }
35379             // columnWidth fall back to item of first element
35380             Roo.log("set column width?");
35381                         this.initialColumnWidth = this.columnWidth  ;
35382
35383             // if first elem has no width, default to size of container
35384             
35385         }
35386         
35387         
35388         if (this.initialColumnWidth) {
35389             this.columnWidth = this.initialColumnWidth;
35390         }
35391         
35392         
35393             
35394         // column width is fixed at the top - however if container width get's smaller we should
35395         // reduce it...
35396         
35397         // this bit calcs how man columns..
35398             
35399         var columnWidth = this.columnWidth += this.gutter;
35400       
35401         // calculate columns
35402         var containerWidth = this.containerWidth + this.gutter;
35403         
35404         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35405         // fix rounding errors, typically with gutters
35406         var excess = columnWidth - containerWidth % columnWidth;
35407         
35408         
35409         // if overshoot is less than a pixel, round up, otherwise floor it
35410         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35411         cols = Math[ mathMethod ]( cols );
35412         this.cols = Math.max( cols, 1 );
35413         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35414         
35415          // padding positioning..
35416         var totalColWidth = this.cols * this.columnWidth;
35417         var padavail = this.containerWidth - totalColWidth;
35418         // so for 2 columns - we need 3 'pads'
35419         
35420         var padNeeded = (1+this.cols) * this.padWidth;
35421         
35422         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35423         
35424         this.columnWidth += padExtra
35425         //this.padWidth = Math.floor(padavail /  ( this.cols));
35426         
35427         // adjust colum width so that padding is fixed??
35428         
35429         // we have 3 columns ... total = width * 3
35430         // we have X left over... that should be used by 
35431         
35432         //if (this.expandC) {
35433             
35434         //}
35435         
35436         
35437         
35438     },
35439     
35440     getContainerWidth : function()
35441     {
35442        /* // container is parent if fit width
35443         var container = this.isFitWidth ? this.element.parentNode : this.element;
35444         // check that this.size and size are there
35445         // IE8 triggers resize on body size change, so they might not be
35446         
35447         var size = getSize( container );  //FIXME
35448         this.containerWidth = size && size.innerWidth; //FIXME
35449         */
35450          
35451         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35452         
35453     },
35454     
35455     _getItemLayoutPosition : function( item )  // what is item?
35456     {
35457         // we resize the item to our columnWidth..
35458       
35459         item.setWidth(this.columnWidth);
35460         item.autoBoxAdjust  = false;
35461         
35462         var sz = item.getSize();
35463  
35464         // how many columns does this brick span
35465         var remainder = this.containerWidth % this.columnWidth;
35466         
35467         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35468         // round if off by 1 pixel, otherwise use ceil
35469         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35470         colSpan = Math.min( colSpan, this.cols );
35471         
35472         // normally this should be '1' as we dont' currently allow multi width columns..
35473         
35474         var colGroup = this._getColGroup( colSpan );
35475         // get the minimum Y value from the columns
35476         var minimumY = Math.min.apply( Math, colGroup );
35477         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35478         
35479         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35480          
35481         // position the brick
35482         var position = {
35483             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35484             y: this.currentSize.y + minimumY + this.padHeight
35485         };
35486         
35487         Roo.log(position);
35488         // apply setHeight to necessary columns
35489         var setHeight = minimumY + sz.height + this.padHeight;
35490         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35491         
35492         var setSpan = this.cols + 1 - colGroup.length;
35493         for ( var i = 0; i < setSpan; i++ ) {
35494           this.colYs[ shortColIndex + i ] = setHeight ;
35495         }
35496       
35497         return position;
35498     },
35499     
35500     /**
35501      * @param {Number} colSpan - number of columns the element spans
35502      * @returns {Array} colGroup
35503      */
35504     _getColGroup : function( colSpan )
35505     {
35506         if ( colSpan < 2 ) {
35507           // if brick spans only one column, use all the column Ys
35508           return this.colYs;
35509         }
35510       
35511         var colGroup = [];
35512         // how many different places could this brick fit horizontally
35513         var groupCount = this.cols + 1 - colSpan;
35514         // for each group potential horizontal position
35515         for ( var i = 0; i < groupCount; i++ ) {
35516           // make an array of colY values for that one group
35517           var groupColYs = this.colYs.slice( i, i + colSpan );
35518           // and get the max value of the array
35519           colGroup[i] = Math.max.apply( Math, groupColYs );
35520         }
35521         return colGroup;
35522     },
35523     /*
35524     _manageStamp : function( stamp )
35525     {
35526         var stampSize =  stamp.getSize();
35527         var offset = stamp.getBox();
35528         // get the columns that this stamp affects
35529         var firstX = this.isOriginLeft ? offset.x : offset.right;
35530         var lastX = firstX + stampSize.width;
35531         var firstCol = Math.floor( firstX / this.columnWidth );
35532         firstCol = Math.max( 0, firstCol );
35533         
35534         var lastCol = Math.floor( lastX / this.columnWidth );
35535         // lastCol should not go over if multiple of columnWidth #425
35536         lastCol -= lastX % this.columnWidth ? 0 : 1;
35537         lastCol = Math.min( this.cols - 1, lastCol );
35538         
35539         // set colYs to bottom of the stamp
35540         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35541             stampSize.height;
35542             
35543         for ( var i = firstCol; i <= lastCol; i++ ) {
35544           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35545         }
35546     },
35547     */
35548     
35549     _getContainerSize : function()
35550     {
35551         this.maxY = Math.max.apply( Math, this.colYs );
35552         var size = {
35553             height: this.maxY
35554         };
35555       
35556         if ( this.isFitWidth ) {
35557             size.width = this._getContainerFitWidth();
35558         }
35559       
35560         return size;
35561     },
35562     
35563     _getContainerFitWidth : function()
35564     {
35565         var unusedCols = 0;
35566         // count unused columns
35567         var i = this.cols;
35568         while ( --i ) {
35569           if ( this.colYs[i] !== 0 ) {
35570             break;
35571           }
35572           unusedCols++;
35573         }
35574         // fit container to columns that have been used
35575         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35576     },
35577     
35578     needsResizeLayout : function()
35579     {
35580         var previousWidth = this.containerWidth;
35581         this.getContainerWidth();
35582         return previousWidth !== this.containerWidth;
35583     }
35584  
35585 });
35586
35587  
35588
35589  /*
35590  * - LGPL
35591  *
35592  * element
35593  * 
35594  */
35595
35596 /**
35597  * @class Roo.bootstrap.MasonryBrick
35598  * @extends Roo.bootstrap.Component
35599  * Bootstrap MasonryBrick class
35600  * 
35601  * @constructor
35602  * Create a new MasonryBrick
35603  * @param {Object} config The config object
35604  */
35605
35606 Roo.bootstrap.MasonryBrick = function(config){
35607     
35608     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35609     
35610     Roo.bootstrap.MasonryBrick.register(this);
35611     
35612     this.addEvents({
35613         // raw events
35614         /**
35615          * @event click
35616          * When a MasonryBrick is clcik
35617          * @param {Roo.bootstrap.MasonryBrick} this
35618          * @param {Roo.EventObject} e
35619          */
35620         "click" : true
35621     });
35622 };
35623
35624 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35625     
35626     /**
35627      * @cfg {String} title
35628      */   
35629     title : '',
35630     /**
35631      * @cfg {String} html
35632      */   
35633     html : '',
35634     /**
35635      * @cfg {String} bgimage
35636      */   
35637     bgimage : '',
35638     /**
35639      * @cfg {String} videourl
35640      */   
35641     videourl : '',
35642     /**
35643      * @cfg {String} cls
35644      */   
35645     cls : '',
35646     /**
35647      * @cfg {String} href
35648      */   
35649     href : '',
35650     /**
35651      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35652      */   
35653     size : 'xs',
35654     
35655     /**
35656      * @cfg {String} placetitle (center|bottom)
35657      */   
35658     placetitle : '',
35659     
35660     /**
35661      * @cfg {Boolean} isFitContainer defalut true
35662      */   
35663     isFitContainer : true, 
35664     
35665     /**
35666      * @cfg {Boolean} preventDefault defalut false
35667      */   
35668     preventDefault : false, 
35669     
35670     /**
35671      * @cfg {Boolean} inverse defalut false
35672      */   
35673     maskInverse : false, 
35674     
35675     getAutoCreate : function()
35676     {
35677         if(!this.isFitContainer){
35678             return this.getSplitAutoCreate();
35679         }
35680         
35681         var cls = 'masonry-brick masonry-brick-full';
35682         
35683         if(this.href.length){
35684             cls += ' masonry-brick-link';
35685         }
35686         
35687         if(this.bgimage.length){
35688             cls += ' masonry-brick-image';
35689         }
35690         
35691         if(this.maskInverse){
35692             cls += ' mask-inverse';
35693         }
35694         
35695         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35696             cls += ' enable-mask';
35697         }
35698         
35699         if(this.size){
35700             cls += ' masonry-' + this.size + '-brick';
35701         }
35702         
35703         if(this.placetitle.length){
35704             
35705             switch (this.placetitle) {
35706                 case 'center' :
35707                     cls += ' masonry-center-title';
35708                     break;
35709                 case 'bottom' :
35710                     cls += ' masonry-bottom-title';
35711                     break;
35712                 default:
35713                     break;
35714             }
35715             
35716         } else {
35717             if(!this.html.length && !this.bgimage.length){
35718                 cls += ' masonry-center-title';
35719             }
35720
35721             if(!this.html.length && this.bgimage.length){
35722                 cls += ' masonry-bottom-title';
35723             }
35724         }
35725         
35726         if(this.cls){
35727             cls += ' ' + this.cls;
35728         }
35729         
35730         var cfg = {
35731             tag: (this.href.length) ? 'a' : 'div',
35732             cls: cls,
35733             cn: [
35734                 {
35735                     tag: 'div',
35736                     cls: 'masonry-brick-mask'
35737                 },
35738                 {
35739                     tag: 'div',
35740                     cls: 'masonry-brick-paragraph',
35741                     cn: []
35742                 }
35743             ]
35744         };
35745         
35746         if(this.href.length){
35747             cfg.href = this.href;
35748         }
35749         
35750         var cn = cfg.cn[1].cn;
35751         
35752         if(this.title.length){
35753             cn.push({
35754                 tag: 'h4',
35755                 cls: 'masonry-brick-title',
35756                 html: this.title
35757             });
35758         }
35759         
35760         if(this.html.length){
35761             cn.push({
35762                 tag: 'p',
35763                 cls: 'masonry-brick-text',
35764                 html: this.html
35765             });
35766         }
35767         
35768         if (!this.title.length && !this.html.length) {
35769             cfg.cn[1].cls += ' hide';
35770         }
35771         
35772         if(this.bgimage.length){
35773             cfg.cn.push({
35774                 tag: 'img',
35775                 cls: 'masonry-brick-image-view',
35776                 src: this.bgimage
35777             });
35778         }
35779         
35780         if(this.videourl.length){
35781             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35782             // youtube support only?
35783             cfg.cn.push({
35784                 tag: 'iframe',
35785                 cls: 'masonry-brick-image-view',
35786                 src: vurl,
35787                 frameborder : 0,
35788                 allowfullscreen : true
35789             });
35790         }
35791         
35792         return cfg;
35793         
35794     },
35795     
35796     getSplitAutoCreate : function()
35797     {
35798         var cls = 'masonry-brick masonry-brick-split';
35799         
35800         if(this.href.length){
35801             cls += ' masonry-brick-link';
35802         }
35803         
35804         if(this.bgimage.length){
35805             cls += ' masonry-brick-image';
35806         }
35807         
35808         if(this.size){
35809             cls += ' masonry-' + this.size + '-brick';
35810         }
35811         
35812         switch (this.placetitle) {
35813             case 'center' :
35814                 cls += ' masonry-center-title';
35815                 break;
35816             case 'bottom' :
35817                 cls += ' masonry-bottom-title';
35818                 break;
35819             default:
35820                 if(!this.bgimage.length){
35821                     cls += ' masonry-center-title';
35822                 }
35823
35824                 if(this.bgimage.length){
35825                     cls += ' masonry-bottom-title';
35826                 }
35827                 break;
35828         }
35829         
35830         if(this.cls){
35831             cls += ' ' + this.cls;
35832         }
35833         
35834         var cfg = {
35835             tag: (this.href.length) ? 'a' : 'div',
35836             cls: cls,
35837             cn: [
35838                 {
35839                     tag: 'div',
35840                     cls: 'masonry-brick-split-head',
35841                     cn: [
35842                         {
35843                             tag: 'div',
35844                             cls: 'masonry-brick-paragraph',
35845                             cn: []
35846                         }
35847                     ]
35848                 },
35849                 {
35850                     tag: 'div',
35851                     cls: 'masonry-brick-split-body',
35852                     cn: []
35853                 }
35854             ]
35855         };
35856         
35857         if(this.href.length){
35858             cfg.href = this.href;
35859         }
35860         
35861         if(this.title.length){
35862             cfg.cn[0].cn[0].cn.push({
35863                 tag: 'h4',
35864                 cls: 'masonry-brick-title',
35865                 html: this.title
35866             });
35867         }
35868         
35869         if(this.html.length){
35870             cfg.cn[1].cn.push({
35871                 tag: 'p',
35872                 cls: 'masonry-brick-text',
35873                 html: this.html
35874             });
35875         }
35876
35877         if(this.bgimage.length){
35878             cfg.cn[0].cn.push({
35879                 tag: 'img',
35880                 cls: 'masonry-brick-image-view',
35881                 src: this.bgimage
35882             });
35883         }
35884         
35885         if(this.videourl.length){
35886             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35887             // youtube support only?
35888             cfg.cn[0].cn.cn.push({
35889                 tag: 'iframe',
35890                 cls: 'masonry-brick-image-view',
35891                 src: vurl,
35892                 frameborder : 0,
35893                 allowfullscreen : true
35894             });
35895         }
35896         
35897         return cfg;
35898     },
35899     
35900     initEvents: function() 
35901     {
35902         switch (this.size) {
35903             case 'xs' :
35904                 this.x = 1;
35905                 this.y = 1;
35906                 break;
35907             case 'sm' :
35908                 this.x = 2;
35909                 this.y = 2;
35910                 break;
35911             case 'md' :
35912             case 'md-left' :
35913             case 'md-right' :
35914                 this.x = 3;
35915                 this.y = 3;
35916                 break;
35917             case 'tall' :
35918                 this.x = 2;
35919                 this.y = 3;
35920                 break;
35921             case 'wide' :
35922                 this.x = 3;
35923                 this.y = 2;
35924                 break;
35925             case 'wide-thin' :
35926                 this.x = 3;
35927                 this.y = 1;
35928                 break;
35929                         
35930             default :
35931                 break;
35932         }
35933         
35934         if(Roo.isTouch){
35935             this.el.on('touchstart', this.onTouchStart, this);
35936             this.el.on('touchmove', this.onTouchMove, this);
35937             this.el.on('touchend', this.onTouchEnd, this);
35938             this.el.on('contextmenu', this.onContextMenu, this);
35939         } else {
35940             this.el.on('mouseenter'  ,this.enter, this);
35941             this.el.on('mouseleave', this.leave, this);
35942             this.el.on('click', this.onClick, this);
35943         }
35944         
35945         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35946             this.parent().bricks.push(this);   
35947         }
35948         
35949     },
35950     
35951     onClick: function(e, el)
35952     {
35953         var time = this.endTimer - this.startTimer;
35954         // Roo.log(e.preventDefault());
35955         if(Roo.isTouch){
35956             if(time > 1000){
35957                 e.preventDefault();
35958                 return;
35959             }
35960         }
35961         
35962         if(!this.preventDefault){
35963             return;
35964         }
35965         
35966         e.preventDefault();
35967         
35968         if (this.activeClass != '') {
35969             this.selectBrick();
35970         }
35971         
35972         this.fireEvent('click', this, e);
35973     },
35974     
35975     enter: function(e, el)
35976     {
35977         e.preventDefault();
35978         
35979         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35980             return;
35981         }
35982         
35983         if(this.bgimage.length && this.html.length){
35984             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35985         }
35986     },
35987     
35988     leave: function(e, el)
35989     {
35990         e.preventDefault();
35991         
35992         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35993             return;
35994         }
35995         
35996         if(this.bgimage.length && this.html.length){
35997             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35998         }
35999     },
36000     
36001     onTouchStart: function(e, el)
36002     {
36003 //        e.preventDefault();
36004         
36005         this.touchmoved = false;
36006         
36007         if(!this.isFitContainer){
36008             return;
36009         }
36010         
36011         if(!this.bgimage.length || !this.html.length){
36012             return;
36013         }
36014         
36015         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36016         
36017         this.timer = new Date().getTime();
36018         
36019     },
36020     
36021     onTouchMove: function(e, el)
36022     {
36023         this.touchmoved = true;
36024     },
36025     
36026     onContextMenu : function(e,el)
36027     {
36028         e.preventDefault();
36029         e.stopPropagation();
36030         return false;
36031     },
36032     
36033     onTouchEnd: function(e, el)
36034     {
36035 //        e.preventDefault();
36036         
36037         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36038         
36039             this.leave(e,el);
36040             
36041             return;
36042         }
36043         
36044         if(!this.bgimage.length || !this.html.length){
36045             
36046             if(this.href.length){
36047                 window.location.href = this.href;
36048             }
36049             
36050             return;
36051         }
36052         
36053         if(!this.isFitContainer){
36054             return;
36055         }
36056         
36057         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36058         
36059         window.location.href = this.href;
36060     },
36061     
36062     //selection on single brick only
36063     selectBrick : function() {
36064         
36065         if (!this.parentId) {
36066             return;
36067         }
36068         
36069         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36070         var index = m.selectedBrick.indexOf(this.id);
36071         
36072         if ( index > -1) {
36073             m.selectedBrick.splice(index,1);
36074             this.el.removeClass(this.activeClass);
36075             return;
36076         }
36077         
36078         for(var i = 0; i < m.selectedBrick.length; i++) {
36079             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36080             b.el.removeClass(b.activeClass);
36081         }
36082         
36083         m.selectedBrick = [];
36084         
36085         m.selectedBrick.push(this.id);
36086         this.el.addClass(this.activeClass);
36087         return;
36088     },
36089     
36090     isSelected : function(){
36091         return this.el.hasClass(this.activeClass);
36092         
36093     }
36094 });
36095
36096 Roo.apply(Roo.bootstrap.MasonryBrick, {
36097     
36098     //groups: {},
36099     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36100      /**
36101     * register a Masonry Brick
36102     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36103     */
36104     
36105     register : function(brick)
36106     {
36107         //this.groups[brick.id] = brick;
36108         this.groups.add(brick.id, brick);
36109     },
36110     /**
36111     * fetch a  masonry brick based on the masonry brick ID
36112     * @param {string} the masonry brick to add
36113     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36114     */
36115     
36116     get: function(brick_id) 
36117     {
36118         // if (typeof(this.groups[brick_id]) == 'undefined') {
36119         //     return false;
36120         // }
36121         // return this.groups[brick_id] ;
36122         
36123         if(this.groups.key(brick_id)) {
36124             return this.groups.key(brick_id);
36125         }
36126         
36127         return false;
36128     }
36129     
36130     
36131     
36132 });
36133
36134  /*
36135  * - LGPL
36136  *
36137  * element
36138  * 
36139  */
36140
36141 /**
36142  * @class Roo.bootstrap.Brick
36143  * @extends Roo.bootstrap.Component
36144  * Bootstrap Brick class
36145  * 
36146  * @constructor
36147  * Create a new Brick
36148  * @param {Object} config The config object
36149  */
36150
36151 Roo.bootstrap.Brick = function(config){
36152     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36153     
36154     this.addEvents({
36155         // raw events
36156         /**
36157          * @event click
36158          * When a Brick is click
36159          * @param {Roo.bootstrap.Brick} this
36160          * @param {Roo.EventObject} e
36161          */
36162         "click" : true
36163     });
36164 };
36165
36166 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36167     
36168     /**
36169      * @cfg {String} title
36170      */   
36171     title : '',
36172     /**
36173      * @cfg {String} html
36174      */   
36175     html : '',
36176     /**
36177      * @cfg {String} bgimage
36178      */   
36179     bgimage : '',
36180     /**
36181      * @cfg {String} cls
36182      */   
36183     cls : '',
36184     /**
36185      * @cfg {String} href
36186      */   
36187     href : '',
36188     /**
36189      * @cfg {String} video
36190      */   
36191     video : '',
36192     /**
36193      * @cfg {Boolean} square
36194      */   
36195     square : true,
36196     
36197     getAutoCreate : function()
36198     {
36199         var cls = 'roo-brick';
36200         
36201         if(this.href.length){
36202             cls += ' roo-brick-link';
36203         }
36204         
36205         if(this.bgimage.length){
36206             cls += ' roo-brick-image';
36207         }
36208         
36209         if(!this.html.length && !this.bgimage.length){
36210             cls += ' roo-brick-center-title';
36211         }
36212         
36213         if(!this.html.length && this.bgimage.length){
36214             cls += ' roo-brick-bottom-title';
36215         }
36216         
36217         if(this.cls){
36218             cls += ' ' + this.cls;
36219         }
36220         
36221         var cfg = {
36222             tag: (this.href.length) ? 'a' : 'div',
36223             cls: cls,
36224             cn: [
36225                 {
36226                     tag: 'div',
36227                     cls: 'roo-brick-paragraph',
36228                     cn: []
36229                 }
36230             ]
36231         };
36232         
36233         if(this.href.length){
36234             cfg.href = this.href;
36235         }
36236         
36237         var cn = cfg.cn[0].cn;
36238         
36239         if(this.title.length){
36240             cn.push({
36241                 tag: 'h4',
36242                 cls: 'roo-brick-title',
36243                 html: this.title
36244             });
36245         }
36246         
36247         if(this.html.length){
36248             cn.push({
36249                 tag: 'p',
36250                 cls: 'roo-brick-text',
36251                 html: this.html
36252             });
36253         } else {
36254             cn.cls += ' hide';
36255         }
36256         
36257         if(this.bgimage.length){
36258             cfg.cn.push({
36259                 tag: 'img',
36260                 cls: 'roo-brick-image-view',
36261                 src: this.bgimage
36262             });
36263         }
36264         
36265         return cfg;
36266     },
36267     
36268     initEvents: function() 
36269     {
36270         if(this.title.length || this.html.length){
36271             this.el.on('mouseenter'  ,this.enter, this);
36272             this.el.on('mouseleave', this.leave, this);
36273         }
36274         
36275         Roo.EventManager.onWindowResize(this.resize, this); 
36276         
36277         if(this.bgimage.length){
36278             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36279             this.imageEl.on('load', this.onImageLoad, this);
36280             return;
36281         }
36282         
36283         this.resize();
36284     },
36285     
36286     onImageLoad : function()
36287     {
36288         this.resize();
36289     },
36290     
36291     resize : function()
36292     {
36293         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36294         
36295         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36296         
36297         if(this.bgimage.length){
36298             var image = this.el.select('.roo-brick-image-view', true).first();
36299             
36300             image.setWidth(paragraph.getWidth());
36301             
36302             if(this.square){
36303                 image.setHeight(paragraph.getWidth());
36304             }
36305             
36306             this.el.setHeight(image.getHeight());
36307             paragraph.setHeight(image.getHeight());
36308             
36309         }
36310         
36311     },
36312     
36313     enter: function(e, el)
36314     {
36315         e.preventDefault();
36316         
36317         if(this.bgimage.length){
36318             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36319             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36320         }
36321     },
36322     
36323     leave: function(e, el)
36324     {
36325         e.preventDefault();
36326         
36327         if(this.bgimage.length){
36328             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36329             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36330         }
36331     }
36332     
36333 });
36334
36335  
36336
36337  /*
36338  * - LGPL
36339  *
36340  * Number field 
36341  */
36342
36343 /**
36344  * @class Roo.bootstrap.NumberField
36345  * @extends Roo.bootstrap.Input
36346  * Bootstrap NumberField class
36347  * 
36348  * 
36349  * 
36350  * 
36351  * @constructor
36352  * Create a new NumberField
36353  * @param {Object} config The config object
36354  */
36355
36356 Roo.bootstrap.NumberField = function(config){
36357     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36358 };
36359
36360 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36361     
36362     /**
36363      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36364      */
36365     allowDecimals : true,
36366     /**
36367      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36368      */
36369     decimalSeparator : ".",
36370     /**
36371      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36372      */
36373     decimalPrecision : 2,
36374     /**
36375      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36376      */
36377     allowNegative : true,
36378     
36379     /**
36380      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36381      */
36382     allowZero: true,
36383     /**
36384      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36385      */
36386     minValue : Number.NEGATIVE_INFINITY,
36387     /**
36388      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36389      */
36390     maxValue : Number.MAX_VALUE,
36391     /**
36392      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36393      */
36394     minText : "The minimum value for this field is {0}",
36395     /**
36396      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36397      */
36398     maxText : "The maximum value for this field is {0}",
36399     /**
36400      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36401      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36402      */
36403     nanText : "{0} is not a valid number",
36404     /**
36405      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36406      */
36407     thousandsDelimiter : false,
36408     /**
36409      * @cfg {String} valueAlign alignment of value
36410      */
36411     valueAlign : "left",
36412
36413     getAutoCreate : function()
36414     {
36415         var hiddenInput = {
36416             tag: 'input',
36417             type: 'hidden',
36418             id: Roo.id(),
36419             cls: 'hidden-number-input'
36420         };
36421         
36422         if (this.name) {
36423             hiddenInput.name = this.name;
36424         }
36425         
36426         this.name = '';
36427         
36428         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36429         
36430         this.name = hiddenInput.name;
36431         
36432         if(cfg.cn.length > 0) {
36433             cfg.cn.push(hiddenInput);
36434         }
36435         
36436         return cfg;
36437     },
36438
36439     // private
36440     initEvents : function()
36441     {   
36442         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36443         
36444         var allowed = "0123456789";
36445         
36446         if(this.allowDecimals){
36447             allowed += this.decimalSeparator;
36448         }
36449         
36450         if(this.allowNegative){
36451             allowed += "-";
36452         }
36453         
36454         if(this.thousandsDelimiter) {
36455             allowed += ",";
36456         }
36457         
36458         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36459         
36460         var keyPress = function(e){
36461             
36462             var k = e.getKey();
36463             
36464             var c = e.getCharCode();
36465             
36466             if(
36467                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36468                     allowed.indexOf(String.fromCharCode(c)) === -1
36469             ){
36470                 e.stopEvent();
36471                 return;
36472             }
36473             
36474             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36475                 return;
36476             }
36477             
36478             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36479                 e.stopEvent();
36480             }
36481         };
36482         
36483         this.el.on("keypress", keyPress, this);
36484     },
36485     
36486     validateValue : function(value)
36487     {
36488         
36489         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36490             return false;
36491         }
36492         
36493         var num = this.parseValue(value);
36494         
36495         if(isNaN(num)){
36496             this.markInvalid(String.format(this.nanText, value));
36497             return false;
36498         }
36499         
36500         if(num < this.minValue){
36501             this.markInvalid(String.format(this.minText, this.minValue));
36502             return false;
36503         }
36504         
36505         if(num > this.maxValue){
36506             this.markInvalid(String.format(this.maxText, this.maxValue));
36507             return false;
36508         }
36509         
36510         return true;
36511     },
36512
36513     getValue : function()
36514     {
36515         var v = this.hiddenEl().getValue();
36516         
36517         return this.fixPrecision(this.parseValue(v));
36518     },
36519
36520     parseValue : function(value)
36521     {
36522         if(this.thousandsDelimiter) {
36523             value += "";
36524             r = new RegExp(",", "g");
36525             value = value.replace(r, "");
36526         }
36527         
36528         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36529         return isNaN(value) ? '' : value;
36530     },
36531
36532     fixPrecision : function(value)
36533     {
36534         if(this.thousandsDelimiter) {
36535             value += "";
36536             r = new RegExp(",", "g");
36537             value = value.replace(r, "");
36538         }
36539         
36540         var nan = isNaN(value);
36541         
36542         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36543             return nan ? '' : value;
36544         }
36545         return parseFloat(value).toFixed(this.decimalPrecision);
36546     },
36547
36548     setValue : function(v)
36549     {
36550         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36551         
36552         this.value = v;
36553         
36554         if(this.rendered){
36555             
36556             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36557             
36558             this.inputEl().dom.value = (v == '') ? '' :
36559                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36560             
36561             if(!this.allowZero && v === '0') {
36562                 this.hiddenEl().dom.value = '';
36563                 this.inputEl().dom.value = '';
36564             }
36565             
36566             this.validate();
36567         }
36568     },
36569
36570     decimalPrecisionFcn : function(v)
36571     {
36572         return Math.floor(v);
36573     },
36574
36575     beforeBlur : function()
36576     {
36577         var v = this.parseValue(this.getRawValue());
36578         
36579         if(v || v === 0 || v === ''){
36580             this.setValue(v);
36581         }
36582     },
36583     
36584     hiddenEl : function()
36585     {
36586         return this.el.select('input.hidden-number-input',true).first();
36587     }
36588     
36589 });
36590
36591  
36592
36593 /*
36594 * Licence: LGPL
36595 */
36596
36597 /**
36598  * @class Roo.bootstrap.DocumentSlider
36599  * @extends Roo.bootstrap.Component
36600  * Bootstrap DocumentSlider class
36601  * 
36602  * @constructor
36603  * Create a new DocumentViewer
36604  * @param {Object} config The config object
36605  */
36606
36607 Roo.bootstrap.DocumentSlider = function(config){
36608     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36609     
36610     this.files = [];
36611     
36612     this.addEvents({
36613         /**
36614          * @event initial
36615          * Fire after initEvent
36616          * @param {Roo.bootstrap.DocumentSlider} this
36617          */
36618         "initial" : true,
36619         /**
36620          * @event update
36621          * Fire after update
36622          * @param {Roo.bootstrap.DocumentSlider} this
36623          */
36624         "update" : true,
36625         /**
36626          * @event click
36627          * Fire after click
36628          * @param {Roo.bootstrap.DocumentSlider} this
36629          */
36630         "click" : true
36631     });
36632 };
36633
36634 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36635     
36636     files : false,
36637     
36638     indicator : 0,
36639     
36640     getAutoCreate : function()
36641     {
36642         var cfg = {
36643             tag : 'div',
36644             cls : 'roo-document-slider',
36645             cn : [
36646                 {
36647                     tag : 'div',
36648                     cls : 'roo-document-slider-header',
36649                     cn : [
36650                         {
36651                             tag : 'div',
36652                             cls : 'roo-document-slider-header-title'
36653                         }
36654                     ]
36655                 },
36656                 {
36657                     tag : 'div',
36658                     cls : 'roo-document-slider-body',
36659                     cn : [
36660                         {
36661                             tag : 'div',
36662                             cls : 'roo-document-slider-prev',
36663                             cn : [
36664                                 {
36665                                     tag : 'i',
36666                                     cls : 'fa fa-chevron-left'
36667                                 }
36668                             ]
36669                         },
36670                         {
36671                             tag : 'div',
36672                             cls : 'roo-document-slider-thumb',
36673                             cn : [
36674                                 {
36675                                     tag : 'img',
36676                                     cls : 'roo-document-slider-image'
36677                                 }
36678                             ]
36679                         },
36680                         {
36681                             tag : 'div',
36682                             cls : 'roo-document-slider-next',
36683                             cn : [
36684                                 {
36685                                     tag : 'i',
36686                                     cls : 'fa fa-chevron-right'
36687                                 }
36688                             ]
36689                         }
36690                     ]
36691                 }
36692             ]
36693         };
36694         
36695         return cfg;
36696     },
36697     
36698     initEvents : function()
36699     {
36700         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36701         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36702         
36703         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36704         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36705         
36706         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36707         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36708         
36709         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36710         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36711         
36712         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36713         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36714         
36715         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36716         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36717         
36718         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36719         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36720         
36721         this.thumbEl.on('click', this.onClick, this);
36722         
36723         this.prevIndicator.on('click', this.prev, this);
36724         
36725         this.nextIndicator.on('click', this.next, this);
36726         
36727     },
36728     
36729     initial : function()
36730     {
36731         if(this.files.length){
36732             this.indicator = 1;
36733             this.update()
36734         }
36735         
36736         this.fireEvent('initial', this);
36737     },
36738     
36739     update : function()
36740     {
36741         this.imageEl.attr('src', this.files[this.indicator - 1]);
36742         
36743         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36744         
36745         this.prevIndicator.show();
36746         
36747         if(this.indicator == 1){
36748             this.prevIndicator.hide();
36749         }
36750         
36751         this.nextIndicator.show();
36752         
36753         if(this.indicator == this.files.length){
36754             this.nextIndicator.hide();
36755         }
36756         
36757         this.thumbEl.scrollTo('top');
36758         
36759         this.fireEvent('update', this);
36760     },
36761     
36762     onClick : function(e)
36763     {
36764         e.preventDefault();
36765         
36766         this.fireEvent('click', this);
36767     },
36768     
36769     prev : function(e)
36770     {
36771         e.preventDefault();
36772         
36773         this.indicator = Math.max(1, this.indicator - 1);
36774         
36775         this.update();
36776     },
36777     
36778     next : function(e)
36779     {
36780         e.preventDefault();
36781         
36782         this.indicator = Math.min(this.files.length, this.indicator + 1);
36783         
36784         this.update();
36785     }
36786 });
36787 /*
36788  * - LGPL
36789  *
36790  * RadioSet
36791  *
36792  *
36793  */
36794
36795 /**
36796  * @class Roo.bootstrap.RadioSet
36797  * @extends Roo.bootstrap.Input
36798  * Bootstrap RadioSet class
36799  * @cfg {String} indicatorpos (left|right) default left
36800  * @cfg {Boolean} inline (true|false) inline the element (default true)
36801  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36802  * @constructor
36803  * Create a new RadioSet
36804  * @param {Object} config The config object
36805  */
36806
36807 Roo.bootstrap.RadioSet = function(config){
36808     
36809     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36810     
36811     this.radioes = [];
36812     
36813     Roo.bootstrap.RadioSet.register(this);
36814     
36815     this.addEvents({
36816         /**
36817         * @event check
36818         * Fires when the element is checked or unchecked.
36819         * @param {Roo.bootstrap.RadioSet} this This radio
36820         * @param {Roo.bootstrap.Radio} item The checked item
36821         */
36822        check : true,
36823        /**
36824         * @event click
36825         * Fires when the element is click.
36826         * @param {Roo.bootstrap.RadioSet} this This radio set
36827         * @param {Roo.bootstrap.Radio} item The checked item
36828         * @param {Roo.EventObject} e The event object
36829         */
36830        click : true
36831     });
36832     
36833 };
36834
36835 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36836
36837     radioes : false,
36838     
36839     inline : true,
36840     
36841     weight : '',
36842     
36843     indicatorpos : 'left',
36844     
36845     getAutoCreate : function()
36846     {
36847         var label = {
36848             tag : 'label',
36849             cls : 'roo-radio-set-label',
36850             cn : [
36851                 {
36852                     tag : 'span',
36853                     html : this.fieldLabel
36854                 }
36855             ]
36856         };
36857         if (Roo.bootstrap.version == 3) {
36858             
36859             
36860             if(this.indicatorpos == 'left'){
36861                 label.cn.unshift({
36862                     tag : 'i',
36863                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36864                     tooltip : 'This field is required'
36865                 });
36866             } else {
36867                 label.cn.push({
36868                     tag : 'i',
36869                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36870                     tooltip : 'This field is required'
36871                 });
36872             }
36873         }
36874         var items = {
36875             tag : 'div',
36876             cls : 'roo-radio-set-items'
36877         };
36878         
36879         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36880         
36881         if (align === 'left' && this.fieldLabel.length) {
36882             
36883             items = {
36884                 cls : "roo-radio-set-right", 
36885                 cn: [
36886                     items
36887                 ]
36888             };
36889             
36890             if(this.labelWidth > 12){
36891                 label.style = "width: " + this.labelWidth + 'px';
36892             }
36893             
36894             if(this.labelWidth < 13 && this.labelmd == 0){
36895                 this.labelmd = this.labelWidth;
36896             }
36897             
36898             if(this.labellg > 0){
36899                 label.cls += ' col-lg-' + this.labellg;
36900                 items.cls += ' col-lg-' + (12 - this.labellg);
36901             }
36902             
36903             if(this.labelmd > 0){
36904                 label.cls += ' col-md-' + this.labelmd;
36905                 items.cls += ' col-md-' + (12 - this.labelmd);
36906             }
36907             
36908             if(this.labelsm > 0){
36909                 label.cls += ' col-sm-' + this.labelsm;
36910                 items.cls += ' col-sm-' + (12 - this.labelsm);
36911             }
36912             
36913             if(this.labelxs > 0){
36914                 label.cls += ' col-xs-' + this.labelxs;
36915                 items.cls += ' col-xs-' + (12 - this.labelxs);
36916             }
36917         }
36918         
36919         var cfg = {
36920             tag : 'div',
36921             cls : 'roo-radio-set',
36922             cn : [
36923                 {
36924                     tag : 'input',
36925                     cls : 'roo-radio-set-input',
36926                     type : 'hidden',
36927                     name : this.name,
36928                     value : this.value ? this.value :  ''
36929                 },
36930                 label,
36931                 items
36932             ]
36933         };
36934         
36935         if(this.weight.length){
36936             cfg.cls += ' roo-radio-' + this.weight;
36937         }
36938         
36939         if(this.inline) {
36940             cfg.cls += ' roo-radio-set-inline';
36941         }
36942         
36943         var settings=this;
36944         ['xs','sm','md','lg'].map(function(size){
36945             if (settings[size]) {
36946                 cfg.cls += ' col-' + size + '-' + settings[size];
36947             }
36948         });
36949         
36950         return cfg;
36951         
36952     },
36953
36954     initEvents : function()
36955     {
36956         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36957         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         if(!this.fieldLabel.length){
36960             this.labelEl.hide();
36961         }
36962         
36963         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36964         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36965         
36966         this.indicator = this.indicatorEl();
36967         
36968         if(this.indicator){
36969             this.indicator.addClass('invisible');
36970         }
36971         
36972         this.originalValue = this.getValue();
36973         
36974     },
36975     
36976     inputEl: function ()
36977     {
36978         return this.el.select('.roo-radio-set-input', true).first();
36979     },
36980     
36981     getChildContainer : function()
36982     {
36983         return this.itemsEl;
36984     },
36985     
36986     register : function(item)
36987     {
36988         this.radioes.push(item);
36989         
36990     },
36991     
36992     validate : function()
36993     {   
36994         if(this.getVisibilityEl().hasClass('hidden')){
36995             return true;
36996         }
36997         
36998         var valid = false;
36999         
37000         Roo.each(this.radioes, function(i){
37001             if(!i.checked){
37002                 return;
37003             }
37004             
37005             valid = true;
37006             return false;
37007         });
37008         
37009         if(this.allowBlank) {
37010             return true;
37011         }
37012         
37013         if(this.disabled || valid){
37014             this.markValid();
37015             return true;
37016         }
37017         
37018         this.markInvalid();
37019         return false;
37020         
37021     },
37022     
37023     markValid : function()
37024     {
37025         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37026             this.indicatorEl().removeClass('visible');
37027             this.indicatorEl().addClass('invisible');
37028         }
37029         
37030         
37031         if (Roo.bootstrap.version == 3) {
37032             this.el.removeClass([this.invalidClass, this.validClass]);
37033             this.el.addClass(this.validClass);
37034         } else {
37035             this.el.removeClass(['is-invalid','is-valid']);
37036             this.el.addClass(['is-valid']);
37037         }
37038         this.fireEvent('valid', this);
37039     },
37040     
37041     markInvalid : function(msg)
37042     {
37043         if(this.allowBlank || this.disabled){
37044             return;
37045         }
37046         
37047         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37048             this.indicatorEl().removeClass('invisible');
37049             this.indicatorEl().addClass('visible');
37050         }
37051         if (Roo.bootstrap.version == 3) {
37052             this.el.removeClass([this.invalidClass, this.validClass]);
37053             this.el.addClass(this.invalidClass);
37054         } else {
37055             this.el.removeClass(['is-invalid','is-valid']);
37056             this.el.addClass(['is-invalid']);
37057         }
37058         
37059         this.fireEvent('invalid', this, msg);
37060         
37061     },
37062     
37063     setValue : function(v, suppressEvent)
37064     {   
37065         if(this.value === v){
37066             return;
37067         }
37068         
37069         this.value = v;
37070         
37071         if(this.rendered){
37072             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37073         }
37074         
37075         Roo.each(this.radioes, function(i){
37076             i.checked = false;
37077             i.el.removeClass('checked');
37078         });
37079         
37080         Roo.each(this.radioes, function(i){
37081             
37082             if(i.value === v || i.value.toString() === v.toString()){
37083                 i.checked = true;
37084                 i.el.addClass('checked');
37085                 
37086                 if(suppressEvent !== true){
37087                     this.fireEvent('check', this, i);
37088                 }
37089                 
37090                 return false;
37091             }
37092             
37093         }, this);
37094         
37095         this.validate();
37096     },
37097     
37098     clearInvalid : function(){
37099         
37100         if(!this.el || this.preventMark){
37101             return;
37102         }
37103         
37104         this.el.removeClass([this.invalidClass]);
37105         
37106         this.fireEvent('valid', this);
37107     }
37108     
37109 });
37110
37111 Roo.apply(Roo.bootstrap.RadioSet, {
37112     
37113     groups: {},
37114     
37115     register : function(set)
37116     {
37117         this.groups[set.name] = set;
37118     },
37119     
37120     get: function(name) 
37121     {
37122         if (typeof(this.groups[name]) == 'undefined') {
37123             return false;
37124         }
37125         
37126         return this.groups[name] ;
37127     }
37128     
37129 });
37130 /*
37131  * Based on:
37132  * Ext JS Library 1.1.1
37133  * Copyright(c) 2006-2007, Ext JS, LLC.
37134  *
37135  * Originally Released Under LGPL - original licence link has changed is not relivant.
37136  *
37137  * Fork - LGPL
37138  * <script type="text/javascript">
37139  */
37140
37141
37142 /**
37143  * @class Roo.bootstrap.SplitBar
37144  * @extends Roo.util.Observable
37145  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37146  * <br><br>
37147  * Usage:
37148  * <pre><code>
37149 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37150                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37151 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37152 split.minSize = 100;
37153 split.maxSize = 600;
37154 split.animate = true;
37155 split.on('moved', splitterMoved);
37156 </code></pre>
37157  * @constructor
37158  * Create a new SplitBar
37159  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37160  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37161  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37162  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37163                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37164                         position of the SplitBar).
37165  */
37166 Roo.bootstrap.SplitBar = function(cfg){
37167     
37168     /** @private */
37169     
37170     //{
37171     //  dragElement : elm
37172     //  resizingElement: el,
37173         // optional..
37174     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37175     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37176         // existingProxy ???
37177     //}
37178     
37179     this.el = Roo.get(cfg.dragElement, true);
37180     this.el.dom.unselectable = "on";
37181     /** @private */
37182     this.resizingEl = Roo.get(cfg.resizingElement, true);
37183
37184     /**
37185      * @private
37186      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37187      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37188      * @type Number
37189      */
37190     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37191     
37192     /**
37193      * The minimum size of the resizing element. (Defaults to 0)
37194      * @type Number
37195      */
37196     this.minSize = 0;
37197     
37198     /**
37199      * The maximum size of the resizing element. (Defaults to 2000)
37200      * @type Number
37201      */
37202     this.maxSize = 2000;
37203     
37204     /**
37205      * Whether to animate the transition to the new size
37206      * @type Boolean
37207      */
37208     this.animate = false;
37209     
37210     /**
37211      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37212      * @type Boolean
37213      */
37214     this.useShim = false;
37215     
37216     /** @private */
37217     this.shim = null;
37218     
37219     if(!cfg.existingProxy){
37220         /** @private */
37221         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37222     }else{
37223         this.proxy = Roo.get(cfg.existingProxy).dom;
37224     }
37225     /** @private */
37226     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37227     
37228     /** @private */
37229     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37230     
37231     /** @private */
37232     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37233     
37234     /** @private */
37235     this.dragSpecs = {};
37236     
37237     /**
37238      * @private The adapter to use to positon and resize elements
37239      */
37240     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37241     this.adapter.init(this);
37242     
37243     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37244         /** @private */
37245         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37246         this.el.addClass("roo-splitbar-h");
37247     }else{
37248         /** @private */
37249         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37250         this.el.addClass("roo-splitbar-v");
37251     }
37252     
37253     this.addEvents({
37254         /**
37255          * @event resize
37256          * Fires when the splitter is moved (alias for {@link #event-moved})
37257          * @param {Roo.bootstrap.SplitBar} this
37258          * @param {Number} newSize the new width or height
37259          */
37260         "resize" : true,
37261         /**
37262          * @event moved
37263          * Fires when the splitter is moved
37264          * @param {Roo.bootstrap.SplitBar} this
37265          * @param {Number} newSize the new width or height
37266          */
37267         "moved" : true,
37268         /**
37269          * @event beforeresize
37270          * Fires before the splitter is dragged
37271          * @param {Roo.bootstrap.SplitBar} this
37272          */
37273         "beforeresize" : true,
37274
37275         "beforeapply" : true
37276     });
37277
37278     Roo.util.Observable.call(this);
37279 };
37280
37281 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37282     onStartProxyDrag : function(x, y){
37283         this.fireEvent("beforeresize", this);
37284         if(!this.overlay){
37285             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37286             o.unselectable();
37287             o.enableDisplayMode("block");
37288             // all splitbars share the same overlay
37289             Roo.bootstrap.SplitBar.prototype.overlay = o;
37290         }
37291         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37292         this.overlay.show();
37293         Roo.get(this.proxy).setDisplayed("block");
37294         var size = this.adapter.getElementSize(this);
37295         this.activeMinSize = this.getMinimumSize();;
37296         this.activeMaxSize = this.getMaximumSize();;
37297         var c1 = size - this.activeMinSize;
37298         var c2 = Math.max(this.activeMaxSize - size, 0);
37299         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37300             this.dd.resetConstraints();
37301             this.dd.setXConstraint(
37302                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37303                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37304             );
37305             this.dd.setYConstraint(0, 0);
37306         }else{
37307             this.dd.resetConstraints();
37308             this.dd.setXConstraint(0, 0);
37309             this.dd.setYConstraint(
37310                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37311                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37312             );
37313          }
37314         this.dragSpecs.startSize = size;
37315         this.dragSpecs.startPoint = [x, y];
37316         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37317     },
37318     
37319     /** 
37320      * @private Called after the drag operation by the DDProxy
37321      */
37322     onEndProxyDrag : function(e){
37323         Roo.get(this.proxy).setDisplayed(false);
37324         var endPoint = Roo.lib.Event.getXY(e);
37325         if(this.overlay){
37326             this.overlay.hide();
37327         }
37328         var newSize;
37329         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37330             newSize = this.dragSpecs.startSize + 
37331                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37332                     endPoint[0] - this.dragSpecs.startPoint[0] :
37333                     this.dragSpecs.startPoint[0] - endPoint[0]
37334                 );
37335         }else{
37336             newSize = this.dragSpecs.startSize + 
37337                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37338                     endPoint[1] - this.dragSpecs.startPoint[1] :
37339                     this.dragSpecs.startPoint[1] - endPoint[1]
37340                 );
37341         }
37342         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37343         if(newSize != this.dragSpecs.startSize){
37344             if(this.fireEvent('beforeapply', this, newSize) !== false){
37345                 this.adapter.setElementSize(this, newSize);
37346                 this.fireEvent("moved", this, newSize);
37347                 this.fireEvent("resize", this, newSize);
37348             }
37349         }
37350     },
37351     
37352     /**
37353      * Get the adapter this SplitBar uses
37354      * @return The adapter object
37355      */
37356     getAdapter : function(){
37357         return this.adapter;
37358     },
37359     
37360     /**
37361      * Set the adapter this SplitBar uses
37362      * @param {Object} adapter A SplitBar adapter object
37363      */
37364     setAdapter : function(adapter){
37365         this.adapter = adapter;
37366         this.adapter.init(this);
37367     },
37368     
37369     /**
37370      * Gets the minimum size for the resizing element
37371      * @return {Number} The minimum size
37372      */
37373     getMinimumSize : function(){
37374         return this.minSize;
37375     },
37376     
37377     /**
37378      * Sets the minimum size for the resizing element
37379      * @param {Number} minSize The minimum size
37380      */
37381     setMinimumSize : function(minSize){
37382         this.minSize = minSize;
37383     },
37384     
37385     /**
37386      * Gets the maximum size for the resizing element
37387      * @return {Number} The maximum size
37388      */
37389     getMaximumSize : function(){
37390         return this.maxSize;
37391     },
37392     
37393     /**
37394      * Sets the maximum size for the resizing element
37395      * @param {Number} maxSize The maximum size
37396      */
37397     setMaximumSize : function(maxSize){
37398         this.maxSize = maxSize;
37399     },
37400     
37401     /**
37402      * Sets the initialize size for the resizing element
37403      * @param {Number} size The initial size
37404      */
37405     setCurrentSize : function(size){
37406         var oldAnimate = this.animate;
37407         this.animate = false;
37408         this.adapter.setElementSize(this, size);
37409         this.animate = oldAnimate;
37410     },
37411     
37412     /**
37413      * Destroy this splitbar. 
37414      * @param {Boolean} removeEl True to remove the element
37415      */
37416     destroy : function(removeEl){
37417         if(this.shim){
37418             this.shim.remove();
37419         }
37420         this.dd.unreg();
37421         this.proxy.parentNode.removeChild(this.proxy);
37422         if(removeEl){
37423             this.el.remove();
37424         }
37425     }
37426 });
37427
37428 /**
37429  * @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.
37430  */
37431 Roo.bootstrap.SplitBar.createProxy = function(dir){
37432     var proxy = new Roo.Element(document.createElement("div"));
37433     proxy.unselectable();
37434     var cls = 'roo-splitbar-proxy';
37435     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37436     document.body.appendChild(proxy.dom);
37437     return proxy.dom;
37438 };
37439
37440 /** 
37441  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37442  * Default Adapter. It assumes the splitter and resizing element are not positioned
37443  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37444  */
37445 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37446 };
37447
37448 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37449     // do nothing for now
37450     init : function(s){
37451     
37452     },
37453     /**
37454      * Called before drag operations to get the current size of the resizing element. 
37455      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37456      */
37457      getElementSize : function(s){
37458         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37459             return s.resizingEl.getWidth();
37460         }else{
37461             return s.resizingEl.getHeight();
37462         }
37463     },
37464     
37465     /**
37466      * Called after drag operations to set the size of the resizing element.
37467      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37468      * @param {Number} newSize The new size to set
37469      * @param {Function} onComplete A function to be invoked when resizing is complete
37470      */
37471     setElementSize : function(s, newSize, onComplete){
37472         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37473             if(!s.animate){
37474                 s.resizingEl.setWidth(newSize);
37475                 if(onComplete){
37476                     onComplete(s, newSize);
37477                 }
37478             }else{
37479                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37480             }
37481         }else{
37482             
37483             if(!s.animate){
37484                 s.resizingEl.setHeight(newSize);
37485                 if(onComplete){
37486                     onComplete(s, newSize);
37487                 }
37488             }else{
37489                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37490             }
37491         }
37492     }
37493 };
37494
37495 /** 
37496  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37497  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37498  * Adapter that  moves the splitter element to align with the resized sizing element. 
37499  * Used with an absolute positioned SplitBar.
37500  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37501  * document.body, make sure you assign an id to the body element.
37502  */
37503 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37504     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37505     this.container = Roo.get(container);
37506 };
37507
37508 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37509     init : function(s){
37510         this.basic.init(s);
37511     },
37512     
37513     getElementSize : function(s){
37514         return this.basic.getElementSize(s);
37515     },
37516     
37517     setElementSize : function(s, newSize, onComplete){
37518         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37519     },
37520     
37521     moveSplitter : function(s){
37522         var yes = Roo.bootstrap.SplitBar;
37523         switch(s.placement){
37524             case yes.LEFT:
37525                 s.el.setX(s.resizingEl.getRight());
37526                 break;
37527             case yes.RIGHT:
37528                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37529                 break;
37530             case yes.TOP:
37531                 s.el.setY(s.resizingEl.getBottom());
37532                 break;
37533             case yes.BOTTOM:
37534                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37535                 break;
37536         }
37537     }
37538 };
37539
37540 /**
37541  * Orientation constant - Create a vertical SplitBar
37542  * @static
37543  * @type Number
37544  */
37545 Roo.bootstrap.SplitBar.VERTICAL = 1;
37546
37547 /**
37548  * Orientation constant - Create a horizontal SplitBar
37549  * @static
37550  * @type Number
37551  */
37552 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37553
37554 /**
37555  * Placement constant - The resizing element is to the left of the splitter element
37556  * @static
37557  * @type Number
37558  */
37559 Roo.bootstrap.SplitBar.LEFT = 1;
37560
37561 /**
37562  * Placement constant - The resizing element is to the right of the splitter element
37563  * @static
37564  * @type Number
37565  */
37566 Roo.bootstrap.SplitBar.RIGHT = 2;
37567
37568 /**
37569  * Placement constant - The resizing element is positioned above the splitter element
37570  * @static
37571  * @type Number
37572  */
37573 Roo.bootstrap.SplitBar.TOP = 3;
37574
37575 /**
37576  * Placement constant - The resizing element is positioned under splitter element
37577  * @static
37578  * @type Number
37579  */
37580 Roo.bootstrap.SplitBar.BOTTOM = 4;
37581 Roo.namespace("Roo.bootstrap.layout");/*
37582  * Based on:
37583  * Ext JS Library 1.1.1
37584  * Copyright(c) 2006-2007, Ext JS, LLC.
37585  *
37586  * Originally Released Under LGPL - original licence link has changed is not relivant.
37587  *
37588  * Fork - LGPL
37589  * <script type="text/javascript">
37590  */
37591
37592 /**
37593  * @class Roo.bootstrap.layout.Manager
37594  * @extends Roo.bootstrap.Component
37595  * Base class for layout managers.
37596  */
37597 Roo.bootstrap.layout.Manager = function(config)
37598 {
37599     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37600
37601
37602
37603
37604
37605     /** false to disable window resize monitoring @type Boolean */
37606     this.monitorWindowResize = true;
37607     this.regions = {};
37608     this.addEvents({
37609         /**
37610          * @event layout
37611          * Fires when a layout is performed.
37612          * @param {Roo.LayoutManager} this
37613          */
37614         "layout" : true,
37615         /**
37616          * @event regionresized
37617          * Fires when the user resizes a region.
37618          * @param {Roo.LayoutRegion} region The resized region
37619          * @param {Number} newSize The new size (width for east/west, height for north/south)
37620          */
37621         "regionresized" : true,
37622         /**
37623          * @event regioncollapsed
37624          * Fires when a region is collapsed.
37625          * @param {Roo.LayoutRegion} region The collapsed region
37626          */
37627         "regioncollapsed" : true,
37628         /**
37629          * @event regionexpanded
37630          * Fires when a region is expanded.
37631          * @param {Roo.LayoutRegion} region The expanded region
37632          */
37633         "regionexpanded" : true
37634     });
37635     this.updating = false;
37636
37637     if (config.el) {
37638         this.el = Roo.get(config.el);
37639         this.initEvents();
37640     }
37641
37642 };
37643
37644 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37645
37646
37647     regions : null,
37648
37649     monitorWindowResize : true,
37650
37651
37652     updating : false,
37653
37654
37655     onRender : function(ct, position)
37656     {
37657         if(!this.el){
37658             this.el = Roo.get(ct);
37659             this.initEvents();
37660         }
37661         //this.fireEvent('render',this);
37662     },
37663
37664
37665     initEvents: function()
37666     {
37667
37668
37669         // ie scrollbar fix
37670         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37671             document.body.scroll = "no";
37672         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37673             this.el.position('relative');
37674         }
37675         this.id = this.el.id;
37676         this.el.addClass("roo-layout-container");
37677         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37678         if(this.el.dom != document.body ) {
37679             this.el.on('resize', this.layout,this);
37680             this.el.on('show', this.layout,this);
37681         }
37682
37683     },
37684
37685     /**
37686      * Returns true if this layout is currently being updated
37687      * @return {Boolean}
37688      */
37689     isUpdating : function(){
37690         return this.updating;
37691     },
37692
37693     /**
37694      * Suspend the LayoutManager from doing auto-layouts while
37695      * making multiple add or remove calls
37696      */
37697     beginUpdate : function(){
37698         this.updating = true;
37699     },
37700
37701     /**
37702      * Restore auto-layouts and optionally disable the manager from performing a layout
37703      * @param {Boolean} noLayout true to disable a layout update
37704      */
37705     endUpdate : function(noLayout){
37706         this.updating = false;
37707         if(!noLayout){
37708             this.layout();
37709         }
37710     },
37711
37712     layout: function(){
37713         // abstract...
37714     },
37715
37716     onRegionResized : function(region, newSize){
37717         this.fireEvent("regionresized", region, newSize);
37718         this.layout();
37719     },
37720
37721     onRegionCollapsed : function(region){
37722         this.fireEvent("regioncollapsed", region);
37723     },
37724
37725     onRegionExpanded : function(region){
37726         this.fireEvent("regionexpanded", region);
37727     },
37728
37729     /**
37730      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37731      * performs box-model adjustments.
37732      * @return {Object} The size as an object {width: (the width), height: (the height)}
37733      */
37734     getViewSize : function()
37735     {
37736         var size;
37737         if(this.el.dom != document.body){
37738             size = this.el.getSize();
37739         }else{
37740             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37741         }
37742         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37743         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37744         return size;
37745     },
37746
37747     /**
37748      * Returns the Element this layout is bound to.
37749      * @return {Roo.Element}
37750      */
37751     getEl : function(){
37752         return this.el;
37753     },
37754
37755     /**
37756      * Returns the specified region.
37757      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37758      * @return {Roo.LayoutRegion}
37759      */
37760     getRegion : function(target){
37761         return this.regions[target.toLowerCase()];
37762     },
37763
37764     onWindowResize : function(){
37765         if(this.monitorWindowResize){
37766             this.layout();
37767         }
37768     }
37769 });
37770 /*
37771  * Based on:
37772  * Ext JS Library 1.1.1
37773  * Copyright(c) 2006-2007, Ext JS, LLC.
37774  *
37775  * Originally Released Under LGPL - original licence link has changed is not relivant.
37776  *
37777  * Fork - LGPL
37778  * <script type="text/javascript">
37779  */
37780 /**
37781  * @class Roo.bootstrap.layout.Border
37782  * @extends Roo.bootstrap.layout.Manager
37783  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37784  * please see: examples/bootstrap/nested.html<br><br>
37785  
37786 <b>The container the layout is rendered into can be either the body element or any other element.
37787 If it is not the body element, the container needs to either be an absolute positioned element,
37788 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37789 the container size if it is not the body element.</b>
37790
37791 * @constructor
37792 * Create a new Border
37793 * @param {Object} config Configuration options
37794  */
37795 Roo.bootstrap.layout.Border = function(config){
37796     config = config || {};
37797     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37798     
37799     
37800     
37801     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37802         if(config[region]){
37803             config[region].region = region;
37804             this.addRegion(config[region]);
37805         }
37806     },this);
37807     
37808 };
37809
37810 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37811
37812 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37813     
37814     parent : false, // this might point to a 'nest' or a ???
37815     
37816     /**
37817      * Creates and adds a new region if it doesn't already exist.
37818      * @param {String} target The target region key (north, south, east, west or center).
37819      * @param {Object} config The regions config object
37820      * @return {BorderLayoutRegion} The new region
37821      */
37822     addRegion : function(config)
37823     {
37824         if(!this.regions[config.region]){
37825             var r = this.factory(config);
37826             this.bindRegion(r);
37827         }
37828         return this.regions[config.region];
37829     },
37830
37831     // private (kinda)
37832     bindRegion : function(r){
37833         this.regions[r.config.region] = r;
37834         
37835         r.on("visibilitychange",    this.layout, this);
37836         r.on("paneladded",          this.layout, this);
37837         r.on("panelremoved",        this.layout, this);
37838         r.on("invalidated",         this.layout, this);
37839         r.on("resized",             this.onRegionResized, this);
37840         r.on("collapsed",           this.onRegionCollapsed, this);
37841         r.on("expanded",            this.onRegionExpanded, this);
37842     },
37843
37844     /**
37845      * Performs a layout update.
37846      */
37847     layout : function()
37848     {
37849         if(this.updating) {
37850             return;
37851         }
37852         
37853         // render all the rebions if they have not been done alreayd?
37854         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37855             if(this.regions[region] && !this.regions[region].bodyEl){
37856                 this.regions[region].onRender(this.el)
37857             }
37858         },this);
37859         
37860         var size = this.getViewSize();
37861         var w = size.width;
37862         var h = size.height;
37863         var centerW = w;
37864         var centerH = h;
37865         var centerY = 0;
37866         var centerX = 0;
37867         //var x = 0, y = 0;
37868
37869         var rs = this.regions;
37870         var north = rs["north"];
37871         var south = rs["south"]; 
37872         var west = rs["west"];
37873         var east = rs["east"];
37874         var center = rs["center"];
37875         //if(this.hideOnLayout){ // not supported anymore
37876             //c.el.setStyle("display", "none");
37877         //}
37878         if(north && north.isVisible()){
37879             var b = north.getBox();
37880             var m = north.getMargins();
37881             b.width = w - (m.left+m.right);
37882             b.x = m.left;
37883             b.y = m.top;
37884             centerY = b.height + b.y + m.bottom;
37885             centerH -= centerY;
37886             north.updateBox(this.safeBox(b));
37887         }
37888         if(south && south.isVisible()){
37889             var b = south.getBox();
37890             var m = south.getMargins();
37891             b.width = w - (m.left+m.right);
37892             b.x = m.left;
37893             var totalHeight = (b.height + m.top + m.bottom);
37894             b.y = h - totalHeight + m.top;
37895             centerH -= totalHeight;
37896             south.updateBox(this.safeBox(b));
37897         }
37898         if(west && west.isVisible()){
37899             var b = west.getBox();
37900             var m = west.getMargins();
37901             b.height = centerH - (m.top+m.bottom);
37902             b.x = m.left;
37903             b.y = centerY + m.top;
37904             var totalWidth = (b.width + m.left + m.right);
37905             centerX += totalWidth;
37906             centerW -= totalWidth;
37907             west.updateBox(this.safeBox(b));
37908         }
37909         if(east && east.isVisible()){
37910             var b = east.getBox();
37911             var m = east.getMargins();
37912             b.height = centerH - (m.top+m.bottom);
37913             var totalWidth = (b.width + m.left + m.right);
37914             b.x = w - totalWidth + m.left;
37915             b.y = centerY + m.top;
37916             centerW -= totalWidth;
37917             east.updateBox(this.safeBox(b));
37918         }
37919         if(center){
37920             var m = center.getMargins();
37921             var centerBox = {
37922                 x: centerX + m.left,
37923                 y: centerY + m.top,
37924                 width: centerW - (m.left+m.right),
37925                 height: centerH - (m.top+m.bottom)
37926             };
37927             //if(this.hideOnLayout){
37928                 //center.el.setStyle("display", "block");
37929             //}
37930             center.updateBox(this.safeBox(centerBox));
37931         }
37932         this.el.repaint();
37933         this.fireEvent("layout", this);
37934     },
37935
37936     // private
37937     safeBox : function(box){
37938         box.width = Math.max(0, box.width);
37939         box.height = Math.max(0, box.height);
37940         return box;
37941     },
37942
37943     /**
37944      * Adds a ContentPanel (or subclass) to this layout.
37945      * @param {String} target The target region key (north, south, east, west or center).
37946      * @param {Roo.ContentPanel} panel The panel to add
37947      * @return {Roo.ContentPanel} The added panel
37948      */
37949     add : function(target, panel){
37950          
37951         target = target.toLowerCase();
37952         return this.regions[target].add(panel);
37953     },
37954
37955     /**
37956      * Remove a ContentPanel (or subclass) to this layout.
37957      * @param {String} target The target region key (north, south, east, west or center).
37958      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37959      * @return {Roo.ContentPanel} The removed panel
37960      */
37961     remove : function(target, panel){
37962         target = target.toLowerCase();
37963         return this.regions[target].remove(panel);
37964     },
37965
37966     /**
37967      * Searches all regions for a panel with the specified id
37968      * @param {String} panelId
37969      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37970      */
37971     findPanel : function(panelId){
37972         var rs = this.regions;
37973         for(var target in rs){
37974             if(typeof rs[target] != "function"){
37975                 var p = rs[target].getPanel(panelId);
37976                 if(p){
37977                     return p;
37978                 }
37979             }
37980         }
37981         return null;
37982     },
37983
37984     /**
37985      * Searches all regions for a panel with the specified id and activates (shows) it.
37986      * @param {String/ContentPanel} panelId The panels id or the panel itself
37987      * @return {Roo.ContentPanel} The shown panel or null
37988      */
37989     showPanel : function(panelId) {
37990       var rs = this.regions;
37991       for(var target in rs){
37992          var r = rs[target];
37993          if(typeof r != "function"){
37994             if(r.hasPanel(panelId)){
37995                return r.showPanel(panelId);
37996             }
37997          }
37998       }
37999       return null;
38000    },
38001
38002    /**
38003      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38004      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38005      */
38006    /*
38007     restoreState : function(provider){
38008         if(!provider){
38009             provider = Roo.state.Manager;
38010         }
38011         var sm = new Roo.LayoutStateManager();
38012         sm.init(this, provider);
38013     },
38014 */
38015  
38016  
38017     /**
38018      * Adds a xtype elements to the layout.
38019      * <pre><code>
38020
38021 layout.addxtype({
38022        xtype : 'ContentPanel',
38023        region: 'west',
38024        items: [ .... ]
38025    }
38026 );
38027
38028 layout.addxtype({
38029         xtype : 'NestedLayoutPanel',
38030         region: 'west',
38031         layout: {
38032            center: { },
38033            west: { }   
38034         },
38035         items : [ ... list of content panels or nested layout panels.. ]
38036    }
38037 );
38038 </code></pre>
38039      * @param {Object} cfg Xtype definition of item to add.
38040      */
38041     addxtype : function(cfg)
38042     {
38043         // basically accepts a pannel...
38044         // can accept a layout region..!?!?
38045         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38046         
38047         
38048         // theory?  children can only be panels??
38049         
38050         //if (!cfg.xtype.match(/Panel$/)) {
38051         //    return false;
38052         //}
38053         var ret = false;
38054         
38055         if (typeof(cfg.region) == 'undefined') {
38056             Roo.log("Failed to add Panel, region was not set");
38057             Roo.log(cfg);
38058             return false;
38059         }
38060         var region = cfg.region;
38061         delete cfg.region;
38062         
38063           
38064         var xitems = [];
38065         if (cfg.items) {
38066             xitems = cfg.items;
38067             delete cfg.items;
38068         }
38069         var nb = false;
38070         
38071         if ( region == 'center') {
38072             Roo.log("Center: " + cfg.title);
38073         }
38074         
38075         
38076         switch(cfg.xtype) 
38077         {
38078             case 'Content':  // ContentPanel (el, cfg)
38079             case 'Scroll':  // ContentPanel (el, cfg)
38080             case 'View': 
38081                 cfg.autoCreate = cfg.autoCreate || true;
38082                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38083                 //} else {
38084                 //    var el = this.el.createChild();
38085                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38086                 //}
38087                 
38088                 this.add(region, ret);
38089                 break;
38090             
38091             /*
38092             case 'TreePanel': // our new panel!
38093                 cfg.el = this.el.createChild();
38094                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38095                 this.add(region, ret);
38096                 break;
38097             */
38098             
38099             case 'Nest': 
38100                 // create a new Layout (which is  a Border Layout...
38101                 
38102                 var clayout = cfg.layout;
38103                 clayout.el  = this.el.createChild();
38104                 clayout.items   = clayout.items  || [];
38105                 
38106                 delete cfg.layout;
38107                 
38108                 // replace this exitems with the clayout ones..
38109                 xitems = clayout.items;
38110                  
38111                 // force background off if it's in center...
38112                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38113                     cfg.background = false;
38114                 }
38115                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38116                 
38117                 
38118                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38119                 //console.log('adding nested layout panel '  + cfg.toSource());
38120                 this.add(region, ret);
38121                 nb = {}; /// find first...
38122                 break;
38123             
38124             case 'Grid':
38125                 
38126                 // needs grid and region
38127                 
38128                 //var el = this.getRegion(region).el.createChild();
38129                 /*
38130                  *var el = this.el.createChild();
38131                 // create the grid first...
38132                 cfg.grid.container = el;
38133                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38134                 */
38135                 
38136                 if (region == 'center' && this.active ) {
38137                     cfg.background = false;
38138                 }
38139                 
38140                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38141                 
38142                 this.add(region, ret);
38143                 /*
38144                 if (cfg.background) {
38145                     // render grid on panel activation (if panel background)
38146                     ret.on('activate', function(gp) {
38147                         if (!gp.grid.rendered) {
38148                     //        gp.grid.render(el);
38149                         }
38150                     });
38151                 } else {
38152                   //  cfg.grid.render(el);
38153                 }
38154                 */
38155                 break;
38156            
38157            
38158             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38159                 // it was the old xcomponent building that caused this before.
38160                 // espeically if border is the top element in the tree.
38161                 ret = this;
38162                 break; 
38163                 
38164                     
38165                 
38166                 
38167                 
38168             default:
38169                 /*
38170                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38171                     
38172                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38173                     this.add(region, ret);
38174                 } else {
38175                 */
38176                     Roo.log(cfg);
38177                     throw "Can not add '" + cfg.xtype + "' to Border";
38178                     return null;
38179              
38180                                 
38181              
38182         }
38183         this.beginUpdate();
38184         // add children..
38185         var region = '';
38186         var abn = {};
38187         Roo.each(xitems, function(i)  {
38188             region = nb && i.region ? i.region : false;
38189             
38190             var add = ret.addxtype(i);
38191            
38192             if (region) {
38193                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38194                 if (!i.background) {
38195                     abn[region] = nb[region] ;
38196                 }
38197             }
38198             
38199         });
38200         this.endUpdate();
38201
38202         // make the last non-background panel active..
38203         //if (nb) { Roo.log(abn); }
38204         if (nb) {
38205             
38206             for(var r in abn) {
38207                 region = this.getRegion(r);
38208                 if (region) {
38209                     // tried using nb[r], but it does not work..
38210                      
38211                     region.showPanel(abn[r]);
38212                    
38213                 }
38214             }
38215         }
38216         return ret;
38217         
38218     },
38219     
38220     
38221 // private
38222     factory : function(cfg)
38223     {
38224         
38225         var validRegions = Roo.bootstrap.layout.Border.regions;
38226
38227         var target = cfg.region;
38228         cfg.mgr = this;
38229         
38230         var r = Roo.bootstrap.layout;
38231         Roo.log(target);
38232         switch(target){
38233             case "north":
38234                 return new r.North(cfg);
38235             case "south":
38236                 return new r.South(cfg);
38237             case "east":
38238                 return new r.East(cfg);
38239             case "west":
38240                 return new r.West(cfg);
38241             case "center":
38242                 return new r.Center(cfg);
38243         }
38244         throw 'Layout region "'+target+'" not supported.';
38245     }
38246     
38247     
38248 });
38249  /*
38250  * Based on:
38251  * Ext JS Library 1.1.1
38252  * Copyright(c) 2006-2007, Ext JS, LLC.
38253  *
38254  * Originally Released Under LGPL - original licence link has changed is not relivant.
38255  *
38256  * Fork - LGPL
38257  * <script type="text/javascript">
38258  */
38259  
38260 /**
38261  * @class Roo.bootstrap.layout.Basic
38262  * @extends Roo.util.Observable
38263  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38264  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38265  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38266  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38267  * @cfg {string}   region  the region that it inhabits..
38268  * @cfg {bool}   skipConfig skip config?
38269  * 
38270
38271  */
38272 Roo.bootstrap.layout.Basic = function(config){
38273     
38274     this.mgr = config.mgr;
38275     
38276     this.position = config.region;
38277     
38278     var skipConfig = config.skipConfig;
38279     
38280     this.events = {
38281         /**
38282          * @scope Roo.BasicLayoutRegion
38283          */
38284         
38285         /**
38286          * @event beforeremove
38287          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38288          * @param {Roo.LayoutRegion} this
38289          * @param {Roo.ContentPanel} panel The panel
38290          * @param {Object} e The cancel event object
38291          */
38292         "beforeremove" : true,
38293         /**
38294          * @event invalidated
38295          * Fires when the layout for this region is changed.
38296          * @param {Roo.LayoutRegion} this
38297          */
38298         "invalidated" : true,
38299         /**
38300          * @event visibilitychange
38301          * Fires when this region is shown or hidden 
38302          * @param {Roo.LayoutRegion} this
38303          * @param {Boolean} visibility true or false
38304          */
38305         "visibilitychange" : true,
38306         /**
38307          * @event paneladded
38308          * Fires when a panel is added. 
38309          * @param {Roo.LayoutRegion} this
38310          * @param {Roo.ContentPanel} panel The panel
38311          */
38312         "paneladded" : true,
38313         /**
38314          * @event panelremoved
38315          * Fires when a panel is removed. 
38316          * @param {Roo.LayoutRegion} this
38317          * @param {Roo.ContentPanel} panel The panel
38318          */
38319         "panelremoved" : true,
38320         /**
38321          * @event beforecollapse
38322          * Fires when this region before collapse.
38323          * @param {Roo.LayoutRegion} this
38324          */
38325         "beforecollapse" : true,
38326         /**
38327          * @event collapsed
38328          * Fires when this region is collapsed.
38329          * @param {Roo.LayoutRegion} this
38330          */
38331         "collapsed" : true,
38332         /**
38333          * @event expanded
38334          * Fires when this region is expanded.
38335          * @param {Roo.LayoutRegion} this
38336          */
38337         "expanded" : true,
38338         /**
38339          * @event slideshow
38340          * Fires when this region is slid into view.
38341          * @param {Roo.LayoutRegion} this
38342          */
38343         "slideshow" : true,
38344         /**
38345          * @event slidehide
38346          * Fires when this region slides out of view. 
38347          * @param {Roo.LayoutRegion} this
38348          */
38349         "slidehide" : true,
38350         /**
38351          * @event panelactivated
38352          * Fires when a panel is activated. 
38353          * @param {Roo.LayoutRegion} this
38354          * @param {Roo.ContentPanel} panel The activated panel
38355          */
38356         "panelactivated" : true,
38357         /**
38358          * @event resized
38359          * Fires when the user resizes this region. 
38360          * @param {Roo.LayoutRegion} this
38361          * @param {Number} newSize The new size (width for east/west, height for north/south)
38362          */
38363         "resized" : true
38364     };
38365     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38366     this.panels = new Roo.util.MixedCollection();
38367     this.panels.getKey = this.getPanelId.createDelegate(this);
38368     this.box = null;
38369     this.activePanel = null;
38370     // ensure listeners are added...
38371     
38372     if (config.listeners || config.events) {
38373         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38374             listeners : config.listeners || {},
38375             events : config.events || {}
38376         });
38377     }
38378     
38379     if(skipConfig !== true){
38380         this.applyConfig(config);
38381     }
38382 };
38383
38384 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38385 {
38386     getPanelId : function(p){
38387         return p.getId();
38388     },
38389     
38390     applyConfig : function(config){
38391         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38392         this.config = config;
38393         
38394     },
38395     
38396     /**
38397      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38398      * the width, for horizontal (north, south) the height.
38399      * @param {Number} newSize The new width or height
38400      */
38401     resizeTo : function(newSize){
38402         var el = this.el ? this.el :
38403                  (this.activePanel ? this.activePanel.getEl() : null);
38404         if(el){
38405             switch(this.position){
38406                 case "east":
38407                 case "west":
38408                     el.setWidth(newSize);
38409                     this.fireEvent("resized", this, newSize);
38410                 break;
38411                 case "north":
38412                 case "south":
38413                     el.setHeight(newSize);
38414                     this.fireEvent("resized", this, newSize);
38415                 break;                
38416             }
38417         }
38418     },
38419     
38420     getBox : function(){
38421         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38422     },
38423     
38424     getMargins : function(){
38425         return this.margins;
38426     },
38427     
38428     updateBox : function(box){
38429         this.box = box;
38430         var el = this.activePanel.getEl();
38431         el.dom.style.left = box.x + "px";
38432         el.dom.style.top = box.y + "px";
38433         this.activePanel.setSize(box.width, box.height);
38434     },
38435     
38436     /**
38437      * Returns the container element for this region.
38438      * @return {Roo.Element}
38439      */
38440     getEl : function(){
38441         return this.activePanel;
38442     },
38443     
38444     /**
38445      * Returns true if this region is currently visible.
38446      * @return {Boolean}
38447      */
38448     isVisible : function(){
38449         return this.activePanel ? true : false;
38450     },
38451     
38452     setActivePanel : function(panel){
38453         panel = this.getPanel(panel);
38454         if(this.activePanel && this.activePanel != panel){
38455             this.activePanel.setActiveState(false);
38456             this.activePanel.getEl().setLeftTop(-10000,-10000);
38457         }
38458         this.activePanel = panel;
38459         panel.setActiveState(true);
38460         if(this.box){
38461             panel.setSize(this.box.width, this.box.height);
38462         }
38463         this.fireEvent("panelactivated", this, panel);
38464         this.fireEvent("invalidated");
38465     },
38466     
38467     /**
38468      * Show the specified panel.
38469      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38470      * @return {Roo.ContentPanel} The shown panel or null
38471      */
38472     showPanel : function(panel){
38473         panel = this.getPanel(panel);
38474         if(panel){
38475             this.setActivePanel(panel);
38476         }
38477         return panel;
38478     },
38479     
38480     /**
38481      * Get the active panel for this region.
38482      * @return {Roo.ContentPanel} The active panel or null
38483      */
38484     getActivePanel : function(){
38485         return this.activePanel;
38486     },
38487     
38488     /**
38489      * Add the passed ContentPanel(s)
38490      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38491      * @return {Roo.ContentPanel} The panel added (if only one was added)
38492      */
38493     add : function(panel){
38494         if(arguments.length > 1){
38495             for(var i = 0, len = arguments.length; i < len; i++) {
38496                 this.add(arguments[i]);
38497             }
38498             return null;
38499         }
38500         if(this.hasPanel(panel)){
38501             this.showPanel(panel);
38502             return panel;
38503         }
38504         var el = panel.getEl();
38505         if(el.dom.parentNode != this.mgr.el.dom){
38506             this.mgr.el.dom.appendChild(el.dom);
38507         }
38508         if(panel.setRegion){
38509             panel.setRegion(this);
38510         }
38511         this.panels.add(panel);
38512         el.setStyle("position", "absolute");
38513         if(!panel.background){
38514             this.setActivePanel(panel);
38515             if(this.config.initialSize && this.panels.getCount()==1){
38516                 this.resizeTo(this.config.initialSize);
38517             }
38518         }
38519         this.fireEvent("paneladded", this, panel);
38520         return panel;
38521     },
38522     
38523     /**
38524      * Returns true if the panel is in this region.
38525      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38526      * @return {Boolean}
38527      */
38528     hasPanel : function(panel){
38529         if(typeof panel == "object"){ // must be panel obj
38530             panel = panel.getId();
38531         }
38532         return this.getPanel(panel) ? true : false;
38533     },
38534     
38535     /**
38536      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38537      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38538      * @param {Boolean} preservePanel Overrides the config preservePanel option
38539      * @return {Roo.ContentPanel} The panel that was removed
38540      */
38541     remove : function(panel, preservePanel){
38542         panel = this.getPanel(panel);
38543         if(!panel){
38544             return null;
38545         }
38546         var e = {};
38547         this.fireEvent("beforeremove", this, panel, e);
38548         if(e.cancel === true){
38549             return null;
38550         }
38551         var panelId = panel.getId();
38552         this.panels.removeKey(panelId);
38553         return panel;
38554     },
38555     
38556     /**
38557      * Returns the panel specified or null if it's not in this region.
38558      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38559      * @return {Roo.ContentPanel}
38560      */
38561     getPanel : function(id){
38562         if(typeof id == "object"){ // must be panel obj
38563             return id;
38564         }
38565         return this.panels.get(id);
38566     },
38567     
38568     /**
38569      * Returns this regions position (north/south/east/west/center).
38570      * @return {String} 
38571      */
38572     getPosition: function(){
38573         return this.position;    
38574     }
38575 });/*
38576  * Based on:
38577  * Ext JS Library 1.1.1
38578  * Copyright(c) 2006-2007, Ext JS, LLC.
38579  *
38580  * Originally Released Under LGPL - original licence link has changed is not relivant.
38581  *
38582  * Fork - LGPL
38583  * <script type="text/javascript">
38584  */
38585  
38586 /**
38587  * @class Roo.bootstrap.layout.Region
38588  * @extends Roo.bootstrap.layout.Basic
38589  * This class represents a region in a layout manager.
38590  
38591  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38592  * @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})
38593  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38594  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38595  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38596  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38597  * @cfg {String}    title           The title for the region (overrides panel titles)
38598  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38599  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38600  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38601  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38602  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38603  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38604  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38605  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38606  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38607  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38608
38609  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38610  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38611  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38612  * @cfg {Number}    width           For East/West panels
38613  * @cfg {Number}    height          For North/South panels
38614  * @cfg {Boolean}   split           To show the splitter
38615  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38616  * 
38617  * @cfg {string}   cls             Extra CSS classes to add to region
38618  * 
38619  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38620  * @cfg {string}   region  the region that it inhabits..
38621  *
38622
38623  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38624  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38625
38626  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38627  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38628  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38629  */
38630 Roo.bootstrap.layout.Region = function(config)
38631 {
38632     this.applyConfig(config);
38633
38634     var mgr = config.mgr;
38635     var pos = config.region;
38636     config.skipConfig = true;
38637     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38638     
38639     if (mgr.el) {
38640         this.onRender(mgr.el);   
38641     }
38642      
38643     this.visible = true;
38644     this.collapsed = false;
38645     this.unrendered_panels = [];
38646 };
38647
38648 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38649
38650     position: '', // set by wrapper (eg. north/south etc..)
38651     unrendered_panels : null,  // unrendered panels.
38652     
38653     tabPosition : false,
38654     
38655     mgr: false, // points to 'Border'
38656     
38657     
38658     createBody : function(){
38659         /** This region's body element 
38660         * @type Roo.Element */
38661         this.bodyEl = this.el.createChild({
38662                 tag: "div",
38663                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38664         });
38665     },
38666
38667     onRender: function(ctr, pos)
38668     {
38669         var dh = Roo.DomHelper;
38670         /** This region's container element 
38671         * @type Roo.Element */
38672         this.el = dh.append(ctr.dom, {
38673                 tag: "div",
38674                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38675             }, true);
38676         /** This region's title element 
38677         * @type Roo.Element */
38678     
38679         this.titleEl = dh.append(this.el.dom,  {
38680                 tag: "div",
38681                 unselectable: "on",
38682                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38683                 children:[
38684                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38685                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38686                 ]
38687             }, true);
38688         
38689         this.titleEl.enableDisplayMode();
38690         /** This region's title text element 
38691         * @type HTMLElement */
38692         this.titleTextEl = this.titleEl.dom.firstChild;
38693         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38694         /*
38695         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38696         this.closeBtn.enableDisplayMode();
38697         this.closeBtn.on("click", this.closeClicked, this);
38698         this.closeBtn.hide();
38699     */
38700         this.createBody(this.config);
38701         if(this.config.hideWhenEmpty){
38702             this.hide();
38703             this.on("paneladded", this.validateVisibility, this);
38704             this.on("panelremoved", this.validateVisibility, this);
38705         }
38706         if(this.autoScroll){
38707             this.bodyEl.setStyle("overflow", "auto");
38708         }else{
38709             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38710         }
38711         //if(c.titlebar !== false){
38712             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38713                 this.titleEl.hide();
38714             }else{
38715                 this.titleEl.show();
38716                 if(this.config.title){
38717                     this.titleTextEl.innerHTML = this.config.title;
38718                 }
38719             }
38720         //}
38721         if(this.config.collapsed){
38722             this.collapse(true);
38723         }
38724         if(this.config.hidden){
38725             this.hide();
38726         }
38727         
38728         if (this.unrendered_panels && this.unrendered_panels.length) {
38729             for (var i =0;i< this.unrendered_panels.length; i++) {
38730                 this.add(this.unrendered_panels[i]);
38731             }
38732             this.unrendered_panels = null;
38733             
38734         }
38735         
38736     },
38737     
38738     applyConfig : function(c)
38739     {
38740         /*
38741          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38742             var dh = Roo.DomHelper;
38743             if(c.titlebar !== false){
38744                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38745                 this.collapseBtn.on("click", this.collapse, this);
38746                 this.collapseBtn.enableDisplayMode();
38747                 /*
38748                 if(c.showPin === true || this.showPin){
38749                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38750                     this.stickBtn.enableDisplayMode();
38751                     this.stickBtn.on("click", this.expand, this);
38752                     this.stickBtn.hide();
38753                 }
38754                 
38755             }
38756             */
38757             /** This region's collapsed element
38758             * @type Roo.Element */
38759             /*
38760              *
38761             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38762                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38763             ]}, true);
38764             
38765             if(c.floatable !== false){
38766                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38767                this.collapsedEl.on("click", this.collapseClick, this);
38768             }
38769
38770             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38771                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38772                    id: "message", unselectable: "on", style:{"float":"left"}});
38773                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38774              }
38775             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38776             this.expandBtn.on("click", this.expand, this);
38777             
38778         }
38779         
38780         if(this.collapseBtn){
38781             this.collapseBtn.setVisible(c.collapsible == true);
38782         }
38783         
38784         this.cmargins = c.cmargins || this.cmargins ||
38785                          (this.position == "west" || this.position == "east" ?
38786                              {top: 0, left: 2, right:2, bottom: 0} :
38787                              {top: 2, left: 0, right:0, bottom: 2});
38788         */
38789         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38790         
38791         
38792         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38793         
38794         this.autoScroll = c.autoScroll || false;
38795         
38796         
38797        
38798         
38799         this.duration = c.duration || .30;
38800         this.slideDuration = c.slideDuration || .45;
38801         this.config = c;
38802        
38803     },
38804     /**
38805      * Returns true if this region is currently visible.
38806      * @return {Boolean}
38807      */
38808     isVisible : function(){
38809         return this.visible;
38810     },
38811
38812     /**
38813      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38814      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38815      */
38816     //setCollapsedTitle : function(title){
38817     //    title = title || "&#160;";
38818      //   if(this.collapsedTitleTextEl){
38819       //      this.collapsedTitleTextEl.innerHTML = title;
38820        // }
38821     //},
38822
38823     getBox : function(){
38824         var b;
38825       //  if(!this.collapsed){
38826             b = this.el.getBox(false, true);
38827        // }else{
38828           //  b = this.collapsedEl.getBox(false, true);
38829         //}
38830         return b;
38831     },
38832
38833     getMargins : function(){
38834         return this.margins;
38835         //return this.collapsed ? this.cmargins : this.margins;
38836     },
38837 /*
38838     highlight : function(){
38839         this.el.addClass("x-layout-panel-dragover");
38840     },
38841
38842     unhighlight : function(){
38843         this.el.removeClass("x-layout-panel-dragover");
38844     },
38845 */
38846     updateBox : function(box)
38847     {
38848         if (!this.bodyEl) {
38849             return; // not rendered yet..
38850         }
38851         
38852         this.box = box;
38853         if(!this.collapsed){
38854             this.el.dom.style.left = box.x + "px";
38855             this.el.dom.style.top = box.y + "px";
38856             this.updateBody(box.width, box.height);
38857         }else{
38858             this.collapsedEl.dom.style.left = box.x + "px";
38859             this.collapsedEl.dom.style.top = box.y + "px";
38860             this.collapsedEl.setSize(box.width, box.height);
38861         }
38862         if(this.tabs){
38863             this.tabs.autoSizeTabs();
38864         }
38865     },
38866
38867     updateBody : function(w, h)
38868     {
38869         if(w !== null){
38870             this.el.setWidth(w);
38871             w -= this.el.getBorderWidth("rl");
38872             if(this.config.adjustments){
38873                 w += this.config.adjustments[0];
38874             }
38875         }
38876         if(h !== null && h > 0){
38877             this.el.setHeight(h);
38878             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38879             h -= this.el.getBorderWidth("tb");
38880             if(this.config.adjustments){
38881                 h += this.config.adjustments[1];
38882             }
38883             this.bodyEl.setHeight(h);
38884             if(this.tabs){
38885                 h = this.tabs.syncHeight(h);
38886             }
38887         }
38888         if(this.panelSize){
38889             w = w !== null ? w : this.panelSize.width;
38890             h = h !== null ? h : this.panelSize.height;
38891         }
38892         if(this.activePanel){
38893             var el = this.activePanel.getEl();
38894             w = w !== null ? w : el.getWidth();
38895             h = h !== null ? h : el.getHeight();
38896             this.panelSize = {width: w, height: h};
38897             this.activePanel.setSize(w, h);
38898         }
38899         if(Roo.isIE && this.tabs){
38900             this.tabs.el.repaint();
38901         }
38902     },
38903
38904     /**
38905      * Returns the container element for this region.
38906      * @return {Roo.Element}
38907      */
38908     getEl : function(){
38909         return this.el;
38910     },
38911
38912     /**
38913      * Hides this region.
38914      */
38915     hide : function(){
38916         //if(!this.collapsed){
38917             this.el.dom.style.left = "-2000px";
38918             this.el.hide();
38919         //}else{
38920          //   this.collapsedEl.dom.style.left = "-2000px";
38921          //   this.collapsedEl.hide();
38922        // }
38923         this.visible = false;
38924         this.fireEvent("visibilitychange", this, false);
38925     },
38926
38927     /**
38928      * Shows this region if it was previously hidden.
38929      */
38930     show : function(){
38931         //if(!this.collapsed){
38932             this.el.show();
38933         //}else{
38934         //    this.collapsedEl.show();
38935        // }
38936         this.visible = true;
38937         this.fireEvent("visibilitychange", this, true);
38938     },
38939 /*
38940     closeClicked : function(){
38941         if(this.activePanel){
38942             this.remove(this.activePanel);
38943         }
38944     },
38945
38946     collapseClick : function(e){
38947         if(this.isSlid){
38948            e.stopPropagation();
38949            this.slideIn();
38950         }else{
38951            e.stopPropagation();
38952            this.slideOut();
38953         }
38954     },
38955 */
38956     /**
38957      * Collapses this region.
38958      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38959      */
38960     /*
38961     collapse : function(skipAnim, skipCheck = false){
38962         if(this.collapsed) {
38963             return;
38964         }
38965         
38966         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38967             
38968             this.collapsed = true;
38969             if(this.split){
38970                 this.split.el.hide();
38971             }
38972             if(this.config.animate && skipAnim !== true){
38973                 this.fireEvent("invalidated", this);
38974                 this.animateCollapse();
38975             }else{
38976                 this.el.setLocation(-20000,-20000);
38977                 this.el.hide();
38978                 this.collapsedEl.show();
38979                 this.fireEvent("collapsed", this);
38980                 this.fireEvent("invalidated", this);
38981             }
38982         }
38983         
38984     },
38985 */
38986     animateCollapse : function(){
38987         // overridden
38988     },
38989
38990     /**
38991      * Expands this region if it was previously collapsed.
38992      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38993      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38994      */
38995     /*
38996     expand : function(e, skipAnim){
38997         if(e) {
38998             e.stopPropagation();
38999         }
39000         if(!this.collapsed || this.el.hasActiveFx()) {
39001             return;
39002         }
39003         if(this.isSlid){
39004             this.afterSlideIn();
39005             skipAnim = true;
39006         }
39007         this.collapsed = false;
39008         if(this.config.animate && skipAnim !== true){
39009             this.animateExpand();
39010         }else{
39011             this.el.show();
39012             if(this.split){
39013                 this.split.el.show();
39014             }
39015             this.collapsedEl.setLocation(-2000,-2000);
39016             this.collapsedEl.hide();
39017             this.fireEvent("invalidated", this);
39018             this.fireEvent("expanded", this);
39019         }
39020     },
39021 */
39022     animateExpand : function(){
39023         // overridden
39024     },
39025
39026     initTabs : function()
39027     {
39028         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39029         
39030         var ts = new Roo.bootstrap.panel.Tabs({
39031             el: this.bodyEl.dom,
39032             region : this,
39033             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39034             disableTooltips: this.config.disableTabTips,
39035             toolbar : this.config.toolbar
39036         });
39037         
39038         if(this.config.hideTabs){
39039             ts.stripWrap.setDisplayed(false);
39040         }
39041         this.tabs = ts;
39042         ts.resizeTabs = this.config.resizeTabs === true;
39043         ts.minTabWidth = this.config.minTabWidth || 40;
39044         ts.maxTabWidth = this.config.maxTabWidth || 250;
39045         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39046         ts.monitorResize = false;
39047         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39048         ts.bodyEl.addClass('roo-layout-tabs-body');
39049         this.panels.each(this.initPanelAsTab, this);
39050     },
39051
39052     initPanelAsTab : function(panel){
39053         var ti = this.tabs.addTab(
39054             panel.getEl().id,
39055             panel.getTitle(),
39056             null,
39057             this.config.closeOnTab && panel.isClosable(),
39058             panel.tpl
39059         );
39060         if(panel.tabTip !== undefined){
39061             ti.setTooltip(panel.tabTip);
39062         }
39063         ti.on("activate", function(){
39064               this.setActivePanel(panel);
39065         }, this);
39066         
39067         if(this.config.closeOnTab){
39068             ti.on("beforeclose", function(t, e){
39069                 e.cancel = true;
39070                 this.remove(panel);
39071             }, this);
39072         }
39073         
39074         panel.tabItem = ti;
39075         
39076         return ti;
39077     },
39078
39079     updatePanelTitle : function(panel, title)
39080     {
39081         if(this.activePanel == panel){
39082             this.updateTitle(title);
39083         }
39084         if(this.tabs){
39085             var ti = this.tabs.getTab(panel.getEl().id);
39086             ti.setText(title);
39087             if(panel.tabTip !== undefined){
39088                 ti.setTooltip(panel.tabTip);
39089             }
39090         }
39091     },
39092
39093     updateTitle : function(title){
39094         if(this.titleTextEl && !this.config.title){
39095             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39096         }
39097     },
39098
39099     setActivePanel : function(panel)
39100     {
39101         panel = this.getPanel(panel);
39102         if(this.activePanel && this.activePanel != panel){
39103             if(this.activePanel.setActiveState(false) === false){
39104                 return;
39105             }
39106         }
39107         this.activePanel = panel;
39108         panel.setActiveState(true);
39109         if(this.panelSize){
39110             panel.setSize(this.panelSize.width, this.panelSize.height);
39111         }
39112         if(this.closeBtn){
39113             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39114         }
39115         this.updateTitle(panel.getTitle());
39116         if(this.tabs){
39117             this.fireEvent("invalidated", this);
39118         }
39119         this.fireEvent("panelactivated", this, panel);
39120     },
39121
39122     /**
39123      * Shows the specified panel.
39124      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39125      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39126      */
39127     showPanel : function(panel)
39128     {
39129         panel = this.getPanel(panel);
39130         if(panel){
39131             if(this.tabs){
39132                 var tab = this.tabs.getTab(panel.getEl().id);
39133                 if(tab.isHidden()){
39134                     this.tabs.unhideTab(tab.id);
39135                 }
39136                 tab.activate();
39137             }else{
39138                 this.setActivePanel(panel);
39139             }
39140         }
39141         return panel;
39142     },
39143
39144     /**
39145      * Get the active panel for this region.
39146      * @return {Roo.ContentPanel} The active panel or null
39147      */
39148     getActivePanel : function(){
39149         return this.activePanel;
39150     },
39151
39152     validateVisibility : function(){
39153         if(this.panels.getCount() < 1){
39154             this.updateTitle("&#160;");
39155             this.closeBtn.hide();
39156             this.hide();
39157         }else{
39158             if(!this.isVisible()){
39159                 this.show();
39160             }
39161         }
39162     },
39163
39164     /**
39165      * Adds the passed ContentPanel(s) to this region.
39166      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39167      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39168      */
39169     add : function(panel)
39170     {
39171         if(arguments.length > 1){
39172             for(var i = 0, len = arguments.length; i < len; i++) {
39173                 this.add(arguments[i]);
39174             }
39175             return null;
39176         }
39177         
39178         // if we have not been rendered yet, then we can not really do much of this..
39179         if (!this.bodyEl) {
39180             this.unrendered_panels.push(panel);
39181             return panel;
39182         }
39183         
39184         
39185         
39186         
39187         if(this.hasPanel(panel)){
39188             this.showPanel(panel);
39189             return panel;
39190         }
39191         panel.setRegion(this);
39192         this.panels.add(panel);
39193        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39194             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39195             // and hide them... ???
39196             this.bodyEl.dom.appendChild(panel.getEl().dom);
39197             if(panel.background !== true){
39198                 this.setActivePanel(panel);
39199             }
39200             this.fireEvent("paneladded", this, panel);
39201             return panel;
39202         }
39203         */
39204         if(!this.tabs){
39205             this.initTabs();
39206         }else{
39207             this.initPanelAsTab(panel);
39208         }
39209         
39210         
39211         if(panel.background !== true){
39212             this.tabs.activate(panel.getEl().id);
39213         }
39214         this.fireEvent("paneladded", this, panel);
39215         return panel;
39216     },
39217
39218     /**
39219      * Hides the tab for the specified panel.
39220      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39221      */
39222     hidePanel : function(panel){
39223         if(this.tabs && (panel = this.getPanel(panel))){
39224             this.tabs.hideTab(panel.getEl().id);
39225         }
39226     },
39227
39228     /**
39229      * Unhides the tab for a previously hidden panel.
39230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39231      */
39232     unhidePanel : function(panel){
39233         if(this.tabs && (panel = this.getPanel(panel))){
39234             this.tabs.unhideTab(panel.getEl().id);
39235         }
39236     },
39237
39238     clearPanels : function(){
39239         while(this.panels.getCount() > 0){
39240              this.remove(this.panels.first());
39241         }
39242     },
39243
39244     /**
39245      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39246      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39247      * @param {Boolean} preservePanel Overrides the config preservePanel option
39248      * @return {Roo.ContentPanel} The panel that was removed
39249      */
39250     remove : function(panel, preservePanel)
39251     {
39252         panel = this.getPanel(panel);
39253         if(!panel){
39254             return null;
39255         }
39256         var e = {};
39257         this.fireEvent("beforeremove", this, panel, e);
39258         if(e.cancel === true){
39259             return null;
39260         }
39261         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39262         var panelId = panel.getId();
39263         this.panels.removeKey(panelId);
39264         if(preservePanel){
39265             document.body.appendChild(panel.getEl().dom);
39266         }
39267         if(this.tabs){
39268             this.tabs.removeTab(panel.getEl().id);
39269         }else if (!preservePanel){
39270             this.bodyEl.dom.removeChild(panel.getEl().dom);
39271         }
39272         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39273             var p = this.panels.first();
39274             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39275             tempEl.appendChild(p.getEl().dom);
39276             this.bodyEl.update("");
39277             this.bodyEl.dom.appendChild(p.getEl().dom);
39278             tempEl = null;
39279             this.updateTitle(p.getTitle());
39280             this.tabs = null;
39281             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39282             this.setActivePanel(p);
39283         }
39284         panel.setRegion(null);
39285         if(this.activePanel == panel){
39286             this.activePanel = null;
39287         }
39288         if(this.config.autoDestroy !== false && preservePanel !== true){
39289             try{panel.destroy();}catch(e){}
39290         }
39291         this.fireEvent("panelremoved", this, panel);
39292         return panel;
39293     },
39294
39295     /**
39296      * Returns the TabPanel component used by this region
39297      * @return {Roo.TabPanel}
39298      */
39299     getTabs : function(){
39300         return this.tabs;
39301     },
39302
39303     createTool : function(parentEl, className){
39304         var btn = Roo.DomHelper.append(parentEl, {
39305             tag: "div",
39306             cls: "x-layout-tools-button",
39307             children: [ {
39308                 tag: "div",
39309                 cls: "roo-layout-tools-button-inner " + className,
39310                 html: "&#160;"
39311             }]
39312         }, true);
39313         btn.addClassOnOver("roo-layout-tools-button-over");
39314         return btn;
39315     }
39316 });/*
39317  * Based on:
39318  * Ext JS Library 1.1.1
39319  * Copyright(c) 2006-2007, Ext JS, LLC.
39320  *
39321  * Originally Released Under LGPL - original licence link has changed is not relivant.
39322  *
39323  * Fork - LGPL
39324  * <script type="text/javascript">
39325  */
39326  
39327
39328
39329 /**
39330  * @class Roo.SplitLayoutRegion
39331  * @extends Roo.LayoutRegion
39332  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39333  */
39334 Roo.bootstrap.layout.Split = function(config){
39335     this.cursor = config.cursor;
39336     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39337 };
39338
39339 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39340 {
39341     splitTip : "Drag to resize.",
39342     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39343     useSplitTips : false,
39344
39345     applyConfig : function(config){
39346         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39347     },
39348     
39349     onRender : function(ctr,pos) {
39350         
39351         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39352         if(!this.config.split){
39353             return;
39354         }
39355         if(!this.split){
39356             
39357             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39358                             tag: "div",
39359                             id: this.el.id + "-split",
39360                             cls: "roo-layout-split roo-layout-split-"+this.position,
39361                             html: "&#160;"
39362             });
39363             /** The SplitBar for this region 
39364             * @type Roo.SplitBar */
39365             // does not exist yet...
39366             Roo.log([this.position, this.orientation]);
39367             
39368             this.split = new Roo.bootstrap.SplitBar({
39369                 dragElement : splitEl,
39370                 resizingElement: this.el,
39371                 orientation : this.orientation
39372             });
39373             
39374             this.split.on("moved", this.onSplitMove, this);
39375             this.split.useShim = this.config.useShim === true;
39376             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39377             if(this.useSplitTips){
39378                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39379             }
39380             //if(config.collapsible){
39381             //    this.split.el.on("dblclick", this.collapse,  this);
39382             //}
39383         }
39384         if(typeof this.config.minSize != "undefined"){
39385             this.split.minSize = this.config.minSize;
39386         }
39387         if(typeof this.config.maxSize != "undefined"){
39388             this.split.maxSize = this.config.maxSize;
39389         }
39390         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39391             this.hideSplitter();
39392         }
39393         
39394     },
39395
39396     getHMaxSize : function(){
39397          var cmax = this.config.maxSize || 10000;
39398          var center = this.mgr.getRegion("center");
39399          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39400     },
39401
39402     getVMaxSize : function(){
39403          var cmax = this.config.maxSize || 10000;
39404          var center = this.mgr.getRegion("center");
39405          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39406     },
39407
39408     onSplitMove : function(split, newSize){
39409         this.fireEvent("resized", this, newSize);
39410     },
39411     
39412     /** 
39413      * Returns the {@link Roo.SplitBar} for this region.
39414      * @return {Roo.SplitBar}
39415      */
39416     getSplitBar : function(){
39417         return this.split;
39418     },
39419     
39420     hide : function(){
39421         this.hideSplitter();
39422         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39423     },
39424
39425     hideSplitter : function(){
39426         if(this.split){
39427             this.split.el.setLocation(-2000,-2000);
39428             this.split.el.hide();
39429         }
39430     },
39431
39432     show : function(){
39433         if(this.split){
39434             this.split.el.show();
39435         }
39436         Roo.bootstrap.layout.Split.superclass.show.call(this);
39437     },
39438     
39439     beforeSlide: function(){
39440         if(Roo.isGecko){// firefox overflow auto bug workaround
39441             this.bodyEl.clip();
39442             if(this.tabs) {
39443                 this.tabs.bodyEl.clip();
39444             }
39445             if(this.activePanel){
39446                 this.activePanel.getEl().clip();
39447                 
39448                 if(this.activePanel.beforeSlide){
39449                     this.activePanel.beforeSlide();
39450                 }
39451             }
39452         }
39453     },
39454     
39455     afterSlide : function(){
39456         if(Roo.isGecko){// firefox overflow auto bug workaround
39457             this.bodyEl.unclip();
39458             if(this.tabs) {
39459                 this.tabs.bodyEl.unclip();
39460             }
39461             if(this.activePanel){
39462                 this.activePanel.getEl().unclip();
39463                 if(this.activePanel.afterSlide){
39464                     this.activePanel.afterSlide();
39465                 }
39466             }
39467         }
39468     },
39469
39470     initAutoHide : function(){
39471         if(this.autoHide !== false){
39472             if(!this.autoHideHd){
39473                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39474                 this.autoHideHd = {
39475                     "mouseout": function(e){
39476                         if(!e.within(this.el, true)){
39477                             st.delay(500);
39478                         }
39479                     },
39480                     "mouseover" : function(e){
39481                         st.cancel();
39482                     },
39483                     scope : this
39484                 };
39485             }
39486             this.el.on(this.autoHideHd);
39487         }
39488     },
39489
39490     clearAutoHide : function(){
39491         if(this.autoHide !== false){
39492             this.el.un("mouseout", this.autoHideHd.mouseout);
39493             this.el.un("mouseover", this.autoHideHd.mouseover);
39494         }
39495     },
39496
39497     clearMonitor : function(){
39498         Roo.get(document).un("click", this.slideInIf, this);
39499     },
39500
39501     // these names are backwards but not changed for compat
39502     slideOut : function(){
39503         if(this.isSlid || this.el.hasActiveFx()){
39504             return;
39505         }
39506         this.isSlid = true;
39507         if(this.collapseBtn){
39508             this.collapseBtn.hide();
39509         }
39510         this.closeBtnState = this.closeBtn.getStyle('display');
39511         this.closeBtn.hide();
39512         if(this.stickBtn){
39513             this.stickBtn.show();
39514         }
39515         this.el.show();
39516         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39517         this.beforeSlide();
39518         this.el.setStyle("z-index", 10001);
39519         this.el.slideIn(this.getSlideAnchor(), {
39520             callback: function(){
39521                 this.afterSlide();
39522                 this.initAutoHide();
39523                 Roo.get(document).on("click", this.slideInIf, this);
39524                 this.fireEvent("slideshow", this);
39525             },
39526             scope: this,
39527             block: true
39528         });
39529     },
39530
39531     afterSlideIn : function(){
39532         this.clearAutoHide();
39533         this.isSlid = false;
39534         this.clearMonitor();
39535         this.el.setStyle("z-index", "");
39536         if(this.collapseBtn){
39537             this.collapseBtn.show();
39538         }
39539         this.closeBtn.setStyle('display', this.closeBtnState);
39540         if(this.stickBtn){
39541             this.stickBtn.hide();
39542         }
39543         this.fireEvent("slidehide", this);
39544     },
39545
39546     slideIn : function(cb){
39547         if(!this.isSlid || this.el.hasActiveFx()){
39548             Roo.callback(cb);
39549             return;
39550         }
39551         this.isSlid = false;
39552         this.beforeSlide();
39553         this.el.slideOut(this.getSlideAnchor(), {
39554             callback: function(){
39555                 this.el.setLeftTop(-10000, -10000);
39556                 this.afterSlide();
39557                 this.afterSlideIn();
39558                 Roo.callback(cb);
39559             },
39560             scope: this,
39561             block: true
39562         });
39563     },
39564     
39565     slideInIf : function(e){
39566         if(!e.within(this.el)){
39567             this.slideIn();
39568         }
39569     },
39570
39571     animateCollapse : function(){
39572         this.beforeSlide();
39573         this.el.setStyle("z-index", 20000);
39574         var anchor = this.getSlideAnchor();
39575         this.el.slideOut(anchor, {
39576             callback : function(){
39577                 this.el.setStyle("z-index", "");
39578                 this.collapsedEl.slideIn(anchor, {duration:.3});
39579                 this.afterSlide();
39580                 this.el.setLocation(-10000,-10000);
39581                 this.el.hide();
39582                 this.fireEvent("collapsed", this);
39583             },
39584             scope: this,
39585             block: true
39586         });
39587     },
39588
39589     animateExpand : function(){
39590         this.beforeSlide();
39591         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39592         this.el.setStyle("z-index", 20000);
39593         this.collapsedEl.hide({
39594             duration:.1
39595         });
39596         this.el.slideIn(this.getSlideAnchor(), {
39597             callback : function(){
39598                 this.el.setStyle("z-index", "");
39599                 this.afterSlide();
39600                 if(this.split){
39601                     this.split.el.show();
39602                 }
39603                 this.fireEvent("invalidated", this);
39604                 this.fireEvent("expanded", this);
39605             },
39606             scope: this,
39607             block: true
39608         });
39609     },
39610
39611     anchors : {
39612         "west" : "left",
39613         "east" : "right",
39614         "north" : "top",
39615         "south" : "bottom"
39616     },
39617
39618     sanchors : {
39619         "west" : "l",
39620         "east" : "r",
39621         "north" : "t",
39622         "south" : "b"
39623     },
39624
39625     canchors : {
39626         "west" : "tl-tr",
39627         "east" : "tr-tl",
39628         "north" : "tl-bl",
39629         "south" : "bl-tl"
39630     },
39631
39632     getAnchor : function(){
39633         return this.anchors[this.position];
39634     },
39635
39636     getCollapseAnchor : function(){
39637         return this.canchors[this.position];
39638     },
39639
39640     getSlideAnchor : function(){
39641         return this.sanchors[this.position];
39642     },
39643
39644     getAlignAdj : function(){
39645         var cm = this.cmargins;
39646         switch(this.position){
39647             case "west":
39648                 return [0, 0];
39649             break;
39650             case "east":
39651                 return [0, 0];
39652             break;
39653             case "north":
39654                 return [0, 0];
39655             break;
39656             case "south":
39657                 return [0, 0];
39658             break;
39659         }
39660     },
39661
39662     getExpandAdj : function(){
39663         var c = this.collapsedEl, cm = this.cmargins;
39664         switch(this.position){
39665             case "west":
39666                 return [-(cm.right+c.getWidth()+cm.left), 0];
39667             break;
39668             case "east":
39669                 return [cm.right+c.getWidth()+cm.left, 0];
39670             break;
39671             case "north":
39672                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39673             break;
39674             case "south":
39675                 return [0, cm.top+cm.bottom+c.getHeight()];
39676             break;
39677         }
39678     }
39679 });/*
39680  * Based on:
39681  * Ext JS Library 1.1.1
39682  * Copyright(c) 2006-2007, Ext JS, LLC.
39683  *
39684  * Originally Released Under LGPL - original licence link has changed is not relivant.
39685  *
39686  * Fork - LGPL
39687  * <script type="text/javascript">
39688  */
39689 /*
39690  * These classes are private internal classes
39691  */
39692 Roo.bootstrap.layout.Center = function(config){
39693     config.region = "center";
39694     Roo.bootstrap.layout.Region.call(this, config);
39695     this.visible = true;
39696     this.minWidth = config.minWidth || 20;
39697     this.minHeight = config.minHeight || 20;
39698 };
39699
39700 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39701     hide : function(){
39702         // center panel can't be hidden
39703     },
39704     
39705     show : function(){
39706         // center panel can't be hidden
39707     },
39708     
39709     getMinWidth: function(){
39710         return this.minWidth;
39711     },
39712     
39713     getMinHeight: function(){
39714         return this.minHeight;
39715     }
39716 });
39717
39718
39719
39720
39721  
39722
39723
39724
39725
39726
39727
39728 Roo.bootstrap.layout.North = function(config)
39729 {
39730     config.region = 'north';
39731     config.cursor = 'n-resize';
39732     
39733     Roo.bootstrap.layout.Split.call(this, config);
39734     
39735     
39736     if(this.split){
39737         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39738         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39739         this.split.el.addClass("roo-layout-split-v");
39740     }
39741     //var size = config.initialSize || config.height;
39742     //if(this.el && typeof size != "undefined"){
39743     //    this.el.setHeight(size);
39744     //}
39745 };
39746 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39747 {
39748     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39749      
39750      
39751     onRender : function(ctr, pos)
39752     {
39753         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39754         var size = this.config.initialSize || this.config.height;
39755         if(this.el && typeof size != "undefined"){
39756             this.el.setHeight(size);
39757         }
39758     
39759     },
39760     
39761     getBox : function(){
39762         if(this.collapsed){
39763             return this.collapsedEl.getBox();
39764         }
39765         var box = this.el.getBox();
39766         if(this.split){
39767             box.height += this.split.el.getHeight();
39768         }
39769         return box;
39770     },
39771     
39772     updateBox : function(box){
39773         if(this.split && !this.collapsed){
39774             box.height -= this.split.el.getHeight();
39775             this.split.el.setLeft(box.x);
39776             this.split.el.setTop(box.y+box.height);
39777             this.split.el.setWidth(box.width);
39778         }
39779         if(this.collapsed){
39780             this.updateBody(box.width, null);
39781         }
39782         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39783     }
39784 });
39785
39786
39787
39788
39789
39790 Roo.bootstrap.layout.South = function(config){
39791     config.region = 'south';
39792     config.cursor = 's-resize';
39793     Roo.bootstrap.layout.Split.call(this, config);
39794     if(this.split){
39795         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39796         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39797         this.split.el.addClass("roo-layout-split-v");
39798     }
39799     
39800 };
39801
39802 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39803     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39804     
39805     onRender : function(ctr, pos)
39806     {
39807         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39808         var size = this.config.initialSize || this.config.height;
39809         if(this.el && typeof size != "undefined"){
39810             this.el.setHeight(size);
39811         }
39812     
39813     },
39814     
39815     getBox : function(){
39816         if(this.collapsed){
39817             return this.collapsedEl.getBox();
39818         }
39819         var box = this.el.getBox();
39820         if(this.split){
39821             var sh = this.split.el.getHeight();
39822             box.height += sh;
39823             box.y -= sh;
39824         }
39825         return box;
39826     },
39827     
39828     updateBox : function(box){
39829         if(this.split && !this.collapsed){
39830             var sh = this.split.el.getHeight();
39831             box.height -= sh;
39832             box.y += sh;
39833             this.split.el.setLeft(box.x);
39834             this.split.el.setTop(box.y-sh);
39835             this.split.el.setWidth(box.width);
39836         }
39837         if(this.collapsed){
39838             this.updateBody(box.width, null);
39839         }
39840         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39841     }
39842 });
39843
39844 Roo.bootstrap.layout.East = function(config){
39845     config.region = "east";
39846     config.cursor = "e-resize";
39847     Roo.bootstrap.layout.Split.call(this, config);
39848     if(this.split){
39849         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39850         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39851         this.split.el.addClass("roo-layout-split-h");
39852     }
39853     
39854 };
39855 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39856     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39857     
39858     onRender : function(ctr, pos)
39859     {
39860         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39861         var size = this.config.initialSize || this.config.width;
39862         if(this.el && typeof size != "undefined"){
39863             this.el.setWidth(size);
39864         }
39865     
39866     },
39867     
39868     getBox : function(){
39869         if(this.collapsed){
39870             return this.collapsedEl.getBox();
39871         }
39872         var box = this.el.getBox();
39873         if(this.split){
39874             var sw = this.split.el.getWidth();
39875             box.width += sw;
39876             box.x -= sw;
39877         }
39878         return box;
39879     },
39880
39881     updateBox : function(box){
39882         if(this.split && !this.collapsed){
39883             var sw = this.split.el.getWidth();
39884             box.width -= sw;
39885             this.split.el.setLeft(box.x);
39886             this.split.el.setTop(box.y);
39887             this.split.el.setHeight(box.height);
39888             box.x += sw;
39889         }
39890         if(this.collapsed){
39891             this.updateBody(null, box.height);
39892         }
39893         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39894     }
39895 });
39896
39897 Roo.bootstrap.layout.West = function(config){
39898     config.region = "west";
39899     config.cursor = "w-resize";
39900     
39901     Roo.bootstrap.layout.Split.call(this, config);
39902     if(this.split){
39903         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39904         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39905         this.split.el.addClass("roo-layout-split-h");
39906     }
39907     
39908 };
39909 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39910     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39911     
39912     onRender: function(ctr, pos)
39913     {
39914         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39915         var size = this.config.initialSize || this.config.width;
39916         if(typeof size != "undefined"){
39917             this.el.setWidth(size);
39918         }
39919     },
39920     
39921     getBox : function(){
39922         if(this.collapsed){
39923             return this.collapsedEl.getBox();
39924         }
39925         var box = this.el.getBox();
39926         if (box.width == 0) {
39927             box.width = this.config.width; // kludge?
39928         }
39929         if(this.split){
39930             box.width += this.split.el.getWidth();
39931         }
39932         return box;
39933     },
39934     
39935     updateBox : function(box){
39936         if(this.split && !this.collapsed){
39937             var sw = this.split.el.getWidth();
39938             box.width -= sw;
39939             this.split.el.setLeft(box.x+box.width);
39940             this.split.el.setTop(box.y);
39941             this.split.el.setHeight(box.height);
39942         }
39943         if(this.collapsed){
39944             this.updateBody(null, box.height);
39945         }
39946         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39947     }
39948 });Roo.namespace("Roo.bootstrap.panel");/*
39949  * Based on:
39950  * Ext JS Library 1.1.1
39951  * Copyright(c) 2006-2007, Ext JS, LLC.
39952  *
39953  * Originally Released Under LGPL - original licence link has changed is not relivant.
39954  *
39955  * Fork - LGPL
39956  * <script type="text/javascript">
39957  */
39958 /**
39959  * @class Roo.ContentPanel
39960  * @extends Roo.util.Observable
39961  * A basic ContentPanel element.
39962  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39963  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39964  * @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
39965  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39966  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39967  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39968  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39969  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39970  * @cfg {String} title          The title for this panel
39971  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39972  * @cfg {String} url            Calls {@link #setUrl} with this value
39973  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39974  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39975  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39976  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39977  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39978  * @cfg {Boolean} badges render the badges
39979  * @cfg {String} cls  extra classes to use  
39980  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39981
39982  * @constructor
39983  * Create a new ContentPanel.
39984  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39985  * @param {String/Object} config A string to set only the title or a config object
39986  * @param {String} content (optional) Set the HTML content for this panel
39987  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39988  */
39989 Roo.bootstrap.panel.Content = function( config){
39990     
39991     this.tpl = config.tpl || false;
39992     
39993     var el = config.el;
39994     var content = config.content;
39995
39996     if(config.autoCreate){ // xtype is available if this is called from factory
39997         el = Roo.id();
39998     }
39999     this.el = Roo.get(el);
40000     if(!this.el && config && config.autoCreate){
40001         if(typeof config.autoCreate == "object"){
40002             if(!config.autoCreate.id){
40003                 config.autoCreate.id = config.id||el;
40004             }
40005             this.el = Roo.DomHelper.append(document.body,
40006                         config.autoCreate, true);
40007         }else{
40008             var elcfg =  {
40009                 tag: "div",
40010                 cls: (config.cls || '') +
40011                     (config.background ? ' bg-' + config.background : '') +
40012                     " roo-layout-inactive-content",
40013                 id: config.id||el
40014             };
40015             if (config.iframe) {
40016                 elcfg.cn = [
40017                     {
40018                         tag : 'iframe',
40019                         style : 'border: 0px',
40020                         src : 'about:blank'
40021                     }
40022                 ];
40023             }
40024               
40025             if (config.html) {
40026                 elcfg.html = config.html;
40027                 
40028             }
40029                         
40030             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40031             if (config.iframe) {
40032                 this.iframeEl = this.el.select('iframe',true).first();
40033             }
40034             
40035         }
40036     } 
40037     this.closable = false;
40038     this.loaded = false;
40039     this.active = false;
40040    
40041       
40042     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40043         
40044         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40045         
40046         this.wrapEl = this.el; //this.el.wrap();
40047         var ti = [];
40048         if (config.toolbar.items) {
40049             ti = config.toolbar.items ;
40050             delete config.toolbar.items ;
40051         }
40052         
40053         var nitems = [];
40054         this.toolbar.render(this.wrapEl, 'before');
40055         for(var i =0;i < ti.length;i++) {
40056           //  Roo.log(['add child', items[i]]);
40057             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40058         }
40059         this.toolbar.items = nitems;
40060         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40061         delete config.toolbar;
40062         
40063     }
40064     /*
40065     // xtype created footer. - not sure if will work as we normally have to render first..
40066     if (this.footer && !this.footer.el && this.footer.xtype) {
40067         if (!this.wrapEl) {
40068             this.wrapEl = this.el.wrap();
40069         }
40070     
40071         this.footer.container = this.wrapEl.createChild();
40072          
40073         this.footer = Roo.factory(this.footer, Roo);
40074         
40075     }
40076     */
40077     
40078      if(typeof config == "string"){
40079         this.title = config;
40080     }else{
40081         Roo.apply(this, config);
40082     }
40083     
40084     if(this.resizeEl){
40085         this.resizeEl = Roo.get(this.resizeEl, true);
40086     }else{
40087         this.resizeEl = this.el;
40088     }
40089     // handle view.xtype
40090     
40091  
40092     
40093     
40094     this.addEvents({
40095         /**
40096          * @event activate
40097          * Fires when this panel is activated. 
40098          * @param {Roo.ContentPanel} this
40099          */
40100         "activate" : true,
40101         /**
40102          * @event deactivate
40103          * Fires when this panel is activated. 
40104          * @param {Roo.ContentPanel} this
40105          */
40106         "deactivate" : true,
40107
40108         /**
40109          * @event resize
40110          * Fires when this panel is resized if fitToFrame is true.
40111          * @param {Roo.ContentPanel} this
40112          * @param {Number} width The width after any component adjustments
40113          * @param {Number} height The height after any component adjustments
40114          */
40115         "resize" : true,
40116         
40117          /**
40118          * @event render
40119          * Fires when this tab is created
40120          * @param {Roo.ContentPanel} this
40121          */
40122         "render" : true
40123         
40124         
40125         
40126     });
40127     
40128
40129     
40130     
40131     if(this.autoScroll && !this.iframe){
40132         this.resizeEl.setStyle("overflow", "auto");
40133     } else {
40134         // fix randome scrolling
40135         //this.el.on('scroll', function() {
40136         //    Roo.log('fix random scolling');
40137         //    this.scrollTo('top',0); 
40138         //});
40139     }
40140     content = content || this.content;
40141     if(content){
40142         this.setContent(content);
40143     }
40144     if(config && config.url){
40145         this.setUrl(this.url, this.params, this.loadOnce);
40146     }
40147     
40148     
40149     
40150     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40151     
40152     if (this.view && typeof(this.view.xtype) != 'undefined') {
40153         this.view.el = this.el.appendChild(document.createElement("div"));
40154         this.view = Roo.factory(this.view); 
40155         this.view.render  &&  this.view.render(false, '');  
40156     }
40157     
40158     
40159     this.fireEvent('render', this);
40160 };
40161
40162 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40163     
40164     cls : '',
40165     background : '',
40166     
40167     tabTip : '',
40168     
40169     iframe : false,
40170     iframeEl : false,
40171     
40172     setRegion : function(region){
40173         this.region = region;
40174         this.setActiveClass(region && !this.background);
40175     },
40176     
40177     
40178     setActiveClass: function(state)
40179     {
40180         if(state){
40181            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40182            this.el.setStyle('position','relative');
40183         }else{
40184            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40185            this.el.setStyle('position', 'absolute');
40186         } 
40187     },
40188     
40189     /**
40190      * Returns the toolbar for this Panel if one was configured. 
40191      * @return {Roo.Toolbar} 
40192      */
40193     getToolbar : function(){
40194         return this.toolbar;
40195     },
40196     
40197     setActiveState : function(active)
40198     {
40199         this.active = active;
40200         this.setActiveClass(active);
40201         if(!active){
40202             if(this.fireEvent("deactivate", this) === false){
40203                 return false;
40204             }
40205             return true;
40206         }
40207         this.fireEvent("activate", this);
40208         return true;
40209     },
40210     /**
40211      * Updates this panel's element (not for iframe)
40212      * @param {String} content The new content
40213      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40214     */
40215     setContent : function(content, loadScripts){
40216         if (this.iframe) {
40217             return;
40218         }
40219         
40220         this.el.update(content, loadScripts);
40221     },
40222
40223     ignoreResize : function(w, h){
40224         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40225             return true;
40226         }else{
40227             this.lastSize = {width: w, height: h};
40228             return false;
40229         }
40230     },
40231     /**
40232      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40233      * @return {Roo.UpdateManager} The UpdateManager
40234      */
40235     getUpdateManager : function(){
40236         if (this.iframe) {
40237             return false;
40238         }
40239         return this.el.getUpdateManager();
40240     },
40241      /**
40242      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40243      * Does not work with IFRAME contents
40244      * @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:
40245 <pre><code>
40246 panel.load({
40247     url: "your-url.php",
40248     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40249     callback: yourFunction,
40250     scope: yourObject, //(optional scope)
40251     discardUrl: false,
40252     nocache: false,
40253     text: "Loading...",
40254     timeout: 30,
40255     scripts: false
40256 });
40257 </code></pre>
40258      
40259      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40260      * 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.
40261      * @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}
40262      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40263      * @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.
40264      * @return {Roo.ContentPanel} this
40265      */
40266     load : function(){
40267         
40268         if (this.iframe) {
40269             return this;
40270         }
40271         
40272         var um = this.el.getUpdateManager();
40273         um.update.apply(um, arguments);
40274         return this;
40275     },
40276
40277
40278     /**
40279      * 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.
40280      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40281      * @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)
40282      * @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)
40283      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40284      */
40285     setUrl : function(url, params, loadOnce){
40286         if (this.iframe) {
40287             this.iframeEl.dom.src = url;
40288             return false;
40289         }
40290         
40291         if(this.refreshDelegate){
40292             this.removeListener("activate", this.refreshDelegate);
40293         }
40294         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40295         this.on("activate", this.refreshDelegate);
40296         return this.el.getUpdateManager();
40297     },
40298     
40299     _handleRefresh : function(url, params, loadOnce){
40300         if(!loadOnce || !this.loaded){
40301             var updater = this.el.getUpdateManager();
40302             updater.update(url, params, this._setLoaded.createDelegate(this));
40303         }
40304     },
40305     
40306     _setLoaded : function(){
40307         this.loaded = true;
40308     }, 
40309     
40310     /**
40311      * Returns this panel's id
40312      * @return {String} 
40313      */
40314     getId : function(){
40315         return this.el.id;
40316     },
40317     
40318     /** 
40319      * Returns this panel's element - used by regiosn to add.
40320      * @return {Roo.Element} 
40321      */
40322     getEl : function(){
40323         return this.wrapEl || this.el;
40324     },
40325     
40326    
40327     
40328     adjustForComponents : function(width, height)
40329     {
40330         //Roo.log('adjustForComponents ');
40331         if(this.resizeEl != this.el){
40332             width -= this.el.getFrameWidth('lr');
40333             height -= this.el.getFrameWidth('tb');
40334         }
40335         if(this.toolbar){
40336             var te = this.toolbar.getEl();
40337             te.setWidth(width);
40338             height -= te.getHeight();
40339         }
40340         if(this.footer){
40341             var te = this.footer.getEl();
40342             te.setWidth(width);
40343             height -= te.getHeight();
40344         }
40345         
40346         
40347         if(this.adjustments){
40348             width += this.adjustments[0];
40349             height += this.adjustments[1];
40350         }
40351         return {"width": width, "height": height};
40352     },
40353     
40354     setSize : function(width, height){
40355         if(this.fitToFrame && !this.ignoreResize(width, height)){
40356             if(this.fitContainer && this.resizeEl != this.el){
40357                 this.el.setSize(width, height);
40358             }
40359             var size = this.adjustForComponents(width, height);
40360             if (this.iframe) {
40361                 this.iframeEl.setSize(width,height);
40362             }
40363             
40364             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40365             this.fireEvent('resize', this, size.width, size.height);
40366             
40367             
40368         }
40369     },
40370     
40371     /**
40372      * Returns this panel's title
40373      * @return {String} 
40374      */
40375     getTitle : function(){
40376         
40377         if (typeof(this.title) != 'object') {
40378             return this.title;
40379         }
40380         
40381         var t = '';
40382         for (var k in this.title) {
40383             if (!this.title.hasOwnProperty(k)) {
40384                 continue;
40385             }
40386             
40387             if (k.indexOf('-') >= 0) {
40388                 var s = k.split('-');
40389                 for (var i = 0; i<s.length; i++) {
40390                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40391                 }
40392             } else {
40393                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40394             }
40395         }
40396         return t;
40397     },
40398     
40399     /**
40400      * Set this panel's title
40401      * @param {String} title
40402      */
40403     setTitle : function(title){
40404         this.title = title;
40405         if(this.region){
40406             this.region.updatePanelTitle(this, title);
40407         }
40408     },
40409     
40410     /**
40411      * Returns true is this panel was configured to be closable
40412      * @return {Boolean} 
40413      */
40414     isClosable : function(){
40415         return this.closable;
40416     },
40417     
40418     beforeSlide : function(){
40419         this.el.clip();
40420         this.resizeEl.clip();
40421     },
40422     
40423     afterSlide : function(){
40424         this.el.unclip();
40425         this.resizeEl.unclip();
40426     },
40427     
40428     /**
40429      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40430      *   Will fail silently if the {@link #setUrl} method has not been called.
40431      *   This does not activate the panel, just updates its content.
40432      */
40433     refresh : function(){
40434         if(this.refreshDelegate){
40435            this.loaded = false;
40436            this.refreshDelegate();
40437         }
40438     },
40439     
40440     /**
40441      * Destroys this panel
40442      */
40443     destroy : function(){
40444         this.el.removeAllListeners();
40445         var tempEl = document.createElement("span");
40446         tempEl.appendChild(this.el.dom);
40447         tempEl.innerHTML = "";
40448         this.el.remove();
40449         this.el = null;
40450     },
40451     
40452     /**
40453      * form - if the content panel contains a form - this is a reference to it.
40454      * @type {Roo.form.Form}
40455      */
40456     form : false,
40457     /**
40458      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40459      *    This contains a reference to it.
40460      * @type {Roo.View}
40461      */
40462     view : false,
40463     
40464       /**
40465      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40466      * <pre><code>
40467
40468 layout.addxtype({
40469        xtype : 'Form',
40470        items: [ .... ]
40471    }
40472 );
40473
40474 </code></pre>
40475      * @param {Object} cfg Xtype definition of item to add.
40476      */
40477     
40478     
40479     getChildContainer: function () {
40480         return this.getEl();
40481     }
40482     
40483     
40484     /*
40485         var  ret = new Roo.factory(cfg);
40486         return ret;
40487         
40488         
40489         // add form..
40490         if (cfg.xtype.match(/^Form$/)) {
40491             
40492             var el;
40493             //if (this.footer) {
40494             //    el = this.footer.container.insertSibling(false, 'before');
40495             //} else {
40496                 el = this.el.createChild();
40497             //}
40498
40499             this.form = new  Roo.form.Form(cfg);
40500             
40501             
40502             if ( this.form.allItems.length) {
40503                 this.form.render(el.dom);
40504             }
40505             return this.form;
40506         }
40507         // should only have one of theses..
40508         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40509             // views.. should not be just added - used named prop 'view''
40510             
40511             cfg.el = this.el.appendChild(document.createElement("div"));
40512             // factory?
40513             
40514             var ret = new Roo.factory(cfg);
40515              
40516              ret.render && ret.render(false, ''); // render blank..
40517             this.view = ret;
40518             return ret;
40519         }
40520         return false;
40521     }
40522     \*/
40523 });
40524  
40525 /**
40526  * @class Roo.bootstrap.panel.Grid
40527  * @extends Roo.bootstrap.panel.Content
40528  * @constructor
40529  * Create a new GridPanel.
40530  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40531  * @param {Object} config A the config object
40532   
40533  */
40534
40535
40536
40537 Roo.bootstrap.panel.Grid = function(config)
40538 {
40539     
40540       
40541     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40542         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40543
40544     config.el = this.wrapper;
40545     //this.el = this.wrapper;
40546     
40547       if (config.container) {
40548         // ctor'ed from a Border/panel.grid
40549         
40550         
40551         this.wrapper.setStyle("overflow", "hidden");
40552         this.wrapper.addClass('roo-grid-container');
40553
40554     }
40555     
40556     
40557     if(config.toolbar){
40558         var tool_el = this.wrapper.createChild();    
40559         this.toolbar = Roo.factory(config.toolbar);
40560         var ti = [];
40561         if (config.toolbar.items) {
40562             ti = config.toolbar.items ;
40563             delete config.toolbar.items ;
40564         }
40565         
40566         var nitems = [];
40567         this.toolbar.render(tool_el);
40568         for(var i =0;i < ti.length;i++) {
40569           //  Roo.log(['add child', items[i]]);
40570             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40571         }
40572         this.toolbar.items = nitems;
40573         
40574         delete config.toolbar;
40575     }
40576     
40577     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40578     config.grid.scrollBody = true;;
40579     config.grid.monitorWindowResize = false; // turn off autosizing
40580     config.grid.autoHeight = false;
40581     config.grid.autoWidth = false;
40582     
40583     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40584     
40585     if (config.background) {
40586         // render grid on panel activation (if panel background)
40587         this.on('activate', function(gp) {
40588             if (!gp.grid.rendered) {
40589                 gp.grid.render(this.wrapper);
40590                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40591             }
40592         });
40593             
40594     } else {
40595         this.grid.render(this.wrapper);
40596         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40597
40598     }
40599     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40600     // ??? needed ??? config.el = this.wrapper;
40601     
40602     
40603     
40604   
40605     // xtype created footer. - not sure if will work as we normally have to render first..
40606     if (this.footer && !this.footer.el && this.footer.xtype) {
40607         
40608         var ctr = this.grid.getView().getFooterPanel(true);
40609         this.footer.dataSource = this.grid.dataSource;
40610         this.footer = Roo.factory(this.footer, Roo);
40611         this.footer.render(ctr);
40612         
40613     }
40614     
40615     
40616     
40617     
40618      
40619 };
40620
40621 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40622     getId : function(){
40623         return this.grid.id;
40624     },
40625     
40626     /**
40627      * Returns the grid for this panel
40628      * @return {Roo.bootstrap.Table} 
40629      */
40630     getGrid : function(){
40631         return this.grid;    
40632     },
40633     
40634     setSize : function(width, height){
40635         if(!this.ignoreResize(width, height)){
40636             var grid = this.grid;
40637             var size = this.adjustForComponents(width, height);
40638             // tfoot is not a footer?
40639           
40640             
40641             var gridel = grid.getGridEl();
40642             gridel.setSize(size.width, size.height);
40643             
40644             var tbd = grid.getGridEl().select('tbody', true).first();
40645             var thd = grid.getGridEl().select('thead',true).first();
40646             var tbf= grid.getGridEl().select('tfoot', true).first();
40647
40648             if (tbf) {
40649                 size.height -= tbf.getHeight();
40650             }
40651             if (thd) {
40652                 size.height -= thd.getHeight();
40653             }
40654             
40655             tbd.setSize(size.width, size.height );
40656             // this is for the account management tab -seems to work there.
40657             var thd = grid.getGridEl().select('thead',true).first();
40658             //if (tbd) {
40659             //    tbd.setSize(size.width, size.height - thd.getHeight());
40660             //}
40661              
40662             grid.autoSize();
40663         }
40664     },
40665      
40666     
40667     
40668     beforeSlide : function(){
40669         this.grid.getView().scroller.clip();
40670     },
40671     
40672     afterSlide : function(){
40673         this.grid.getView().scroller.unclip();
40674     },
40675     
40676     destroy : function(){
40677         this.grid.destroy();
40678         delete this.grid;
40679         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40680     }
40681 });
40682
40683 /**
40684  * @class Roo.bootstrap.panel.Nest
40685  * @extends Roo.bootstrap.panel.Content
40686  * @constructor
40687  * Create a new Panel, that can contain a layout.Border.
40688  * 
40689  * 
40690  * @param {Roo.BorderLayout} layout The layout for this panel
40691  * @param {String/Object} config A string to set only the title or a config object
40692  */
40693 Roo.bootstrap.panel.Nest = function(config)
40694 {
40695     // construct with only one argument..
40696     /* FIXME - implement nicer consturctors
40697     if (layout.layout) {
40698         config = layout;
40699         layout = config.layout;
40700         delete config.layout;
40701     }
40702     if (layout.xtype && !layout.getEl) {
40703         // then layout needs constructing..
40704         layout = Roo.factory(layout, Roo);
40705     }
40706     */
40707     
40708     config.el =  config.layout.getEl();
40709     
40710     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40711     
40712     config.layout.monitorWindowResize = false; // turn off autosizing
40713     this.layout = config.layout;
40714     this.layout.getEl().addClass("roo-layout-nested-layout");
40715     this.layout.parent = this;
40716     
40717     
40718     
40719     
40720 };
40721
40722 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40723
40724     setSize : function(width, height){
40725         if(!this.ignoreResize(width, height)){
40726             var size = this.adjustForComponents(width, height);
40727             var el = this.layout.getEl();
40728             if (size.height < 1) {
40729                 el.setWidth(size.width);   
40730             } else {
40731                 el.setSize(size.width, size.height);
40732             }
40733             var touch = el.dom.offsetWidth;
40734             this.layout.layout();
40735             // ie requires a double layout on the first pass
40736             if(Roo.isIE && !this.initialized){
40737                 this.initialized = true;
40738                 this.layout.layout();
40739             }
40740         }
40741     },
40742     
40743     // activate all subpanels if not currently active..
40744     
40745     setActiveState : function(active){
40746         this.active = active;
40747         this.setActiveClass(active);
40748         
40749         if(!active){
40750             this.fireEvent("deactivate", this);
40751             return;
40752         }
40753         
40754         this.fireEvent("activate", this);
40755         // not sure if this should happen before or after..
40756         if (!this.layout) {
40757             return; // should not happen..
40758         }
40759         var reg = false;
40760         for (var r in this.layout.regions) {
40761             reg = this.layout.getRegion(r);
40762             if (reg.getActivePanel()) {
40763                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40764                 reg.setActivePanel(reg.getActivePanel());
40765                 continue;
40766             }
40767             if (!reg.panels.length) {
40768                 continue;
40769             }
40770             reg.showPanel(reg.getPanel(0));
40771         }
40772         
40773         
40774         
40775         
40776     },
40777     
40778     /**
40779      * Returns the nested BorderLayout for this panel
40780      * @return {Roo.BorderLayout} 
40781      */
40782     getLayout : function(){
40783         return this.layout;
40784     },
40785     
40786      /**
40787      * Adds a xtype elements to the layout of the nested panel
40788      * <pre><code>
40789
40790 panel.addxtype({
40791        xtype : 'ContentPanel',
40792        region: 'west',
40793        items: [ .... ]
40794    }
40795 );
40796
40797 panel.addxtype({
40798         xtype : 'NestedLayoutPanel',
40799         region: 'west',
40800         layout: {
40801            center: { },
40802            west: { }   
40803         },
40804         items : [ ... list of content panels or nested layout panels.. ]
40805    }
40806 );
40807 </code></pre>
40808      * @param {Object} cfg Xtype definition of item to add.
40809      */
40810     addxtype : function(cfg) {
40811         return this.layout.addxtype(cfg);
40812     
40813     }
40814 });/*
40815  * Based on:
40816  * Ext JS Library 1.1.1
40817  * Copyright(c) 2006-2007, Ext JS, LLC.
40818  *
40819  * Originally Released Under LGPL - original licence link has changed is not relivant.
40820  *
40821  * Fork - LGPL
40822  * <script type="text/javascript">
40823  */
40824 /**
40825  * @class Roo.TabPanel
40826  * @extends Roo.util.Observable
40827  * A lightweight tab container.
40828  * <br><br>
40829  * Usage:
40830  * <pre><code>
40831 // basic tabs 1, built from existing content
40832 var tabs = new Roo.TabPanel("tabs1");
40833 tabs.addTab("script", "View Script");
40834 tabs.addTab("markup", "View Markup");
40835 tabs.activate("script");
40836
40837 // more advanced tabs, built from javascript
40838 var jtabs = new Roo.TabPanel("jtabs");
40839 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40840
40841 // set up the UpdateManager
40842 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40843 var updater = tab2.getUpdateManager();
40844 updater.setDefaultUrl("ajax1.htm");
40845 tab2.on('activate', updater.refresh, updater, true);
40846
40847 // Use setUrl for Ajax loading
40848 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40849 tab3.setUrl("ajax2.htm", null, true);
40850
40851 // Disabled tab
40852 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40853 tab4.disable();
40854
40855 jtabs.activate("jtabs-1");
40856  * </code></pre>
40857  * @constructor
40858  * Create a new TabPanel.
40859  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40860  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40861  */
40862 Roo.bootstrap.panel.Tabs = function(config){
40863     /**
40864     * The container element for this TabPanel.
40865     * @type Roo.Element
40866     */
40867     this.el = Roo.get(config.el);
40868     delete config.el;
40869     if(config){
40870         if(typeof config == "boolean"){
40871             this.tabPosition = config ? "bottom" : "top";
40872         }else{
40873             Roo.apply(this, config);
40874         }
40875     }
40876     
40877     if(this.tabPosition == "bottom"){
40878         // if tabs are at the bottom = create the body first.
40879         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40880         this.el.addClass("roo-tabs-bottom");
40881     }
40882     // next create the tabs holders
40883     
40884     if (this.tabPosition == "west"){
40885         
40886         var reg = this.region; // fake it..
40887         while (reg) {
40888             if (!reg.mgr.parent) {
40889                 break;
40890             }
40891             reg = reg.mgr.parent.region;
40892         }
40893         Roo.log("got nest?");
40894         Roo.log(reg);
40895         if (reg.mgr.getRegion('west')) {
40896             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40897             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40898             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40899             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40900             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40901         
40902             
40903         }
40904         
40905         
40906     } else {
40907      
40908         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40909         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40910         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40911         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40912     }
40913     
40914     
40915     if(Roo.isIE){
40916         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40917     }
40918     
40919     // finally - if tabs are at the top, then create the body last..
40920     if(this.tabPosition != "bottom"){
40921         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40922          * @type Roo.Element
40923          */
40924         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40925         this.el.addClass("roo-tabs-top");
40926     }
40927     this.items = [];
40928
40929     this.bodyEl.setStyle("position", "relative");
40930
40931     this.active = null;
40932     this.activateDelegate = this.activate.createDelegate(this);
40933
40934     this.addEvents({
40935         /**
40936          * @event tabchange
40937          * Fires when the active tab changes
40938          * @param {Roo.TabPanel} this
40939          * @param {Roo.TabPanelItem} activePanel The new active tab
40940          */
40941         "tabchange": true,
40942         /**
40943          * @event beforetabchange
40944          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40945          * @param {Roo.TabPanel} this
40946          * @param {Object} e Set cancel to true on this object to cancel the tab change
40947          * @param {Roo.TabPanelItem} tab The tab being changed to
40948          */
40949         "beforetabchange" : true
40950     });
40951
40952     Roo.EventManager.onWindowResize(this.onResize, this);
40953     this.cpad = this.el.getPadding("lr");
40954     this.hiddenCount = 0;
40955
40956
40957     // toolbar on the tabbar support...
40958     if (this.toolbar) {
40959         alert("no toolbar support yet");
40960         this.toolbar  = false;
40961         /*
40962         var tcfg = this.toolbar;
40963         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40964         this.toolbar = new Roo.Toolbar(tcfg);
40965         if (Roo.isSafari) {
40966             var tbl = tcfg.container.child('table', true);
40967             tbl.setAttribute('width', '100%');
40968         }
40969         */
40970         
40971     }
40972    
40973
40974
40975     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40976 };
40977
40978 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40979     /*
40980      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40981      */
40982     tabPosition : "top",
40983     /*
40984      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40985      */
40986     currentTabWidth : 0,
40987     /*
40988      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40989      */
40990     minTabWidth : 40,
40991     /*
40992      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40993      */
40994     maxTabWidth : 250,
40995     /*
40996      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40997      */
40998     preferredTabWidth : 175,
40999     /*
41000      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41001      */
41002     resizeTabs : false,
41003     /*
41004      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41005      */
41006     monitorResize : true,
41007     /*
41008      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41009      */
41010     toolbar : false,  // set by caller..
41011     
41012     region : false, /// set by caller
41013     
41014     disableTooltips : true, // not used yet...
41015
41016     /**
41017      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41018      * @param {String} id The id of the div to use <b>or create</b>
41019      * @param {String} text The text for the tab
41020      * @param {String} content (optional) Content to put in the TabPanelItem body
41021      * @param {Boolean} closable (optional) True to create a close icon on the tab
41022      * @return {Roo.TabPanelItem} The created TabPanelItem
41023      */
41024     addTab : function(id, text, content, closable, tpl)
41025     {
41026         var item = new Roo.bootstrap.panel.TabItem({
41027             panel: this,
41028             id : id,
41029             text : text,
41030             closable : closable,
41031             tpl : tpl
41032         });
41033         this.addTabItem(item);
41034         if(content){
41035             item.setContent(content);
41036         }
41037         return item;
41038     },
41039
41040     /**
41041      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41042      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41043      * @return {Roo.TabPanelItem}
41044      */
41045     getTab : function(id){
41046         return this.items[id];
41047     },
41048
41049     /**
41050      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41051      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41052      */
41053     hideTab : function(id){
41054         var t = this.items[id];
41055         if(!t.isHidden()){
41056            t.setHidden(true);
41057            this.hiddenCount++;
41058            this.autoSizeTabs();
41059         }
41060     },
41061
41062     /**
41063      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41064      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41065      */
41066     unhideTab : function(id){
41067         var t = this.items[id];
41068         if(t.isHidden()){
41069            t.setHidden(false);
41070            this.hiddenCount--;
41071            this.autoSizeTabs();
41072         }
41073     },
41074
41075     /**
41076      * Adds an existing {@link Roo.TabPanelItem}.
41077      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41078      */
41079     addTabItem : function(item)
41080     {
41081         this.items[item.id] = item;
41082         this.items.push(item);
41083         this.autoSizeTabs();
41084       //  if(this.resizeTabs){
41085     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41086   //         this.autoSizeTabs();
41087 //        }else{
41088 //            item.autoSize();
41089        // }
41090     },
41091
41092     /**
41093      * Removes a {@link Roo.TabPanelItem}.
41094      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41095      */
41096     removeTab : function(id){
41097         var items = this.items;
41098         var tab = items[id];
41099         if(!tab) { return; }
41100         var index = items.indexOf(tab);
41101         if(this.active == tab && items.length > 1){
41102             var newTab = this.getNextAvailable(index);
41103             if(newTab) {
41104                 newTab.activate();
41105             }
41106         }
41107         this.stripEl.dom.removeChild(tab.pnode.dom);
41108         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41109             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41110         }
41111         items.splice(index, 1);
41112         delete this.items[tab.id];
41113         tab.fireEvent("close", tab);
41114         tab.purgeListeners();
41115         this.autoSizeTabs();
41116     },
41117
41118     getNextAvailable : function(start){
41119         var items = this.items;
41120         var index = start;
41121         // look for a next tab that will slide over to
41122         // replace the one being removed
41123         while(index < items.length){
41124             var item = items[++index];
41125             if(item && !item.isHidden()){
41126                 return item;
41127             }
41128         }
41129         // if one isn't found select the previous tab (on the left)
41130         index = start;
41131         while(index >= 0){
41132             var item = items[--index];
41133             if(item && !item.isHidden()){
41134                 return item;
41135             }
41136         }
41137         return null;
41138     },
41139
41140     /**
41141      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41142      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41143      */
41144     disableTab : function(id){
41145         var tab = this.items[id];
41146         if(tab && this.active != tab){
41147             tab.disable();
41148         }
41149     },
41150
41151     /**
41152      * Enables a {@link Roo.TabPanelItem} that is disabled.
41153      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41154      */
41155     enableTab : function(id){
41156         var tab = this.items[id];
41157         tab.enable();
41158     },
41159
41160     /**
41161      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41162      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41163      * @return {Roo.TabPanelItem} The TabPanelItem.
41164      */
41165     activate : function(id)
41166     {
41167         //Roo.log('activite:'  + id);
41168         
41169         var tab = this.items[id];
41170         if(!tab){
41171             return null;
41172         }
41173         if(tab == this.active || tab.disabled){
41174             return tab;
41175         }
41176         var e = {};
41177         this.fireEvent("beforetabchange", this, e, tab);
41178         if(e.cancel !== true && !tab.disabled){
41179             if(this.active){
41180                 this.active.hide();
41181             }
41182             this.active = this.items[id];
41183             this.active.show();
41184             this.fireEvent("tabchange", this, this.active);
41185         }
41186         return tab;
41187     },
41188
41189     /**
41190      * Gets the active {@link Roo.TabPanelItem}.
41191      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41192      */
41193     getActiveTab : function(){
41194         return this.active;
41195     },
41196
41197     /**
41198      * Updates the tab body element to fit the height of the container element
41199      * for overflow scrolling
41200      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41201      */
41202     syncHeight : function(targetHeight){
41203         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41204         var bm = this.bodyEl.getMargins();
41205         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41206         this.bodyEl.setHeight(newHeight);
41207         return newHeight;
41208     },
41209
41210     onResize : function(){
41211         if(this.monitorResize){
41212             this.autoSizeTabs();
41213         }
41214     },
41215
41216     /**
41217      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41218      */
41219     beginUpdate : function(){
41220         this.updating = true;
41221     },
41222
41223     /**
41224      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41225      */
41226     endUpdate : function(){
41227         this.updating = false;
41228         this.autoSizeTabs();
41229     },
41230
41231     /**
41232      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41233      */
41234     autoSizeTabs : function()
41235     {
41236         var count = this.items.length;
41237         var vcount = count - this.hiddenCount;
41238         
41239         if (vcount < 2) {
41240             this.stripEl.hide();
41241         } else {
41242             this.stripEl.show();
41243         }
41244         
41245         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41246             return;
41247         }
41248         
41249         
41250         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41251         var availWidth = Math.floor(w / vcount);
41252         var b = this.stripBody;
41253         if(b.getWidth() > w){
41254             var tabs = this.items;
41255             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41256             if(availWidth < this.minTabWidth){
41257                 /*if(!this.sleft){    // incomplete scrolling code
41258                     this.createScrollButtons();
41259                 }
41260                 this.showScroll();
41261                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41262             }
41263         }else{
41264             if(this.currentTabWidth < this.preferredTabWidth){
41265                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41266             }
41267         }
41268     },
41269
41270     /**
41271      * Returns the number of tabs in this TabPanel.
41272      * @return {Number}
41273      */
41274      getCount : function(){
41275          return this.items.length;
41276      },
41277
41278     /**
41279      * Resizes all the tabs to the passed width
41280      * @param {Number} The new width
41281      */
41282     setTabWidth : function(width){
41283         this.currentTabWidth = width;
41284         for(var i = 0, len = this.items.length; i < len; i++) {
41285                 if(!this.items[i].isHidden()) {
41286                 this.items[i].setWidth(width);
41287             }
41288         }
41289     },
41290
41291     /**
41292      * Destroys this TabPanel
41293      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41294      */
41295     destroy : function(removeEl){
41296         Roo.EventManager.removeResizeListener(this.onResize, this);
41297         for(var i = 0, len = this.items.length; i < len; i++){
41298             this.items[i].purgeListeners();
41299         }
41300         if(removeEl === true){
41301             this.el.update("");
41302             this.el.remove();
41303         }
41304     },
41305     
41306     createStrip : function(container)
41307     {
41308         var strip = document.createElement("nav");
41309         strip.className = Roo.bootstrap.version == 4 ?
41310             "navbar-light bg-light" : 
41311             "navbar navbar-default"; //"x-tabs-wrap";
41312         container.appendChild(strip);
41313         return strip;
41314     },
41315     
41316     createStripList : function(strip)
41317     {
41318         // div wrapper for retard IE
41319         // returns the "tr" element.
41320         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41321         //'<div class="x-tabs-strip-wrap">'+
41322           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41323           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41324         return strip.firstChild; //.firstChild.firstChild.firstChild;
41325     },
41326     createBody : function(container)
41327     {
41328         var body = document.createElement("div");
41329         Roo.id(body, "tab-body");
41330         //Roo.fly(body).addClass("x-tabs-body");
41331         Roo.fly(body).addClass("tab-content");
41332         container.appendChild(body);
41333         return body;
41334     },
41335     createItemBody :function(bodyEl, id){
41336         var body = Roo.getDom(id);
41337         if(!body){
41338             body = document.createElement("div");
41339             body.id = id;
41340         }
41341         //Roo.fly(body).addClass("x-tabs-item-body");
41342         Roo.fly(body).addClass("tab-pane");
41343          bodyEl.insertBefore(body, bodyEl.firstChild);
41344         return body;
41345     },
41346     /** @private */
41347     createStripElements :  function(stripEl, text, closable, tpl)
41348     {
41349         var td = document.createElement("li"); // was td..
41350         td.className = 'nav-item';
41351         
41352         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41353         
41354         
41355         stripEl.appendChild(td);
41356         /*if(closable){
41357             td.className = "x-tabs-closable";
41358             if(!this.closeTpl){
41359                 this.closeTpl = new Roo.Template(
41360                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41361                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41362                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41363                 );
41364             }
41365             var el = this.closeTpl.overwrite(td, {"text": text});
41366             var close = el.getElementsByTagName("div")[0];
41367             var inner = el.getElementsByTagName("em")[0];
41368             return {"el": el, "close": close, "inner": inner};
41369         } else {
41370         */
41371         // not sure what this is..
41372 //            if(!this.tabTpl){
41373                 //this.tabTpl = new Roo.Template(
41374                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41375                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41376                 //);
41377 //                this.tabTpl = new Roo.Template(
41378 //                   '<a href="#">' +
41379 //                   '<span unselectable="on"' +
41380 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41381 //                            ' >{text}</span></a>'
41382 //                );
41383 //                
41384 //            }
41385
41386
41387             var template = tpl || this.tabTpl || false;
41388             
41389             if(!template){
41390                 template =  new Roo.Template(
41391                         Roo.bootstrap.version == 4 ? 
41392                             (
41393                                 '<a class="nav-link" href="#" unselectable="on"' +
41394                                      (this.disableTooltips ? '' : ' title="{text}"') +
41395                                      ' >{text}</a>'
41396                             ) : (
41397                                 '<a class="nav-link" href="#">' +
41398                                 '<span unselectable="on"' +
41399                                          (this.disableTooltips ? '' : ' title="{text}"') +
41400                                     ' >{text}</span></a>'
41401                             )
41402                 );
41403             }
41404             
41405             switch (typeof(template)) {
41406                 case 'object' :
41407                     break;
41408                 case 'string' :
41409                     template = new Roo.Template(template);
41410                     break;
41411                 default :
41412                     break;
41413             }
41414             
41415             var el = template.overwrite(td, {"text": text});
41416             
41417             var inner = el.getElementsByTagName("span")[0];
41418             
41419             return {"el": el, "inner": inner};
41420             
41421     }
41422         
41423     
41424 });
41425
41426 /**
41427  * @class Roo.TabPanelItem
41428  * @extends Roo.util.Observable
41429  * Represents an individual item (tab plus body) in a TabPanel.
41430  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41431  * @param {String} id The id of this TabPanelItem
41432  * @param {String} text The text for the tab of this TabPanelItem
41433  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41434  */
41435 Roo.bootstrap.panel.TabItem = function(config){
41436     /**
41437      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41438      * @type Roo.TabPanel
41439      */
41440     this.tabPanel = config.panel;
41441     /**
41442      * The id for this TabPanelItem
41443      * @type String
41444      */
41445     this.id = config.id;
41446     /** @private */
41447     this.disabled = false;
41448     /** @private */
41449     this.text = config.text;
41450     /** @private */
41451     this.loaded = false;
41452     this.closable = config.closable;
41453
41454     /**
41455      * The body element for this TabPanelItem.
41456      * @type Roo.Element
41457      */
41458     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41459     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41460     this.bodyEl.setStyle("display", "block");
41461     this.bodyEl.setStyle("zoom", "1");
41462     //this.hideAction();
41463
41464     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41465     /** @private */
41466     this.el = Roo.get(els.el);
41467     this.inner = Roo.get(els.inner, true);
41468      this.textEl = Roo.bootstrap.version == 4 ?
41469         this.el : Roo.get(this.el.dom.firstChild, true);
41470
41471     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41472     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41473
41474     
41475 //    this.el.on("mousedown", this.onTabMouseDown, this);
41476     this.el.on("click", this.onTabClick, this);
41477     /** @private */
41478     if(config.closable){
41479         var c = Roo.get(els.close, true);
41480         c.dom.title = this.closeText;
41481         c.addClassOnOver("close-over");
41482         c.on("click", this.closeClick, this);
41483      }
41484
41485     this.addEvents({
41486          /**
41487          * @event activate
41488          * Fires when this tab becomes the active tab.
41489          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41490          * @param {Roo.TabPanelItem} this
41491          */
41492         "activate": true,
41493         /**
41494          * @event beforeclose
41495          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41496          * @param {Roo.TabPanelItem} this
41497          * @param {Object} e Set cancel to true on this object to cancel the close.
41498          */
41499         "beforeclose": true,
41500         /**
41501          * @event close
41502          * Fires when this tab is closed.
41503          * @param {Roo.TabPanelItem} this
41504          */
41505          "close": true,
41506         /**
41507          * @event deactivate
41508          * Fires when this tab is no longer the active tab.
41509          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41510          * @param {Roo.TabPanelItem} this
41511          */
41512          "deactivate" : true
41513     });
41514     this.hidden = false;
41515
41516     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41517 };
41518
41519 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41520            {
41521     purgeListeners : function(){
41522        Roo.util.Observable.prototype.purgeListeners.call(this);
41523        this.el.removeAllListeners();
41524     },
41525     /**
41526      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41527      */
41528     show : function(){
41529         this.status_node.addClass("active");
41530         this.showAction();
41531         if(Roo.isOpera){
41532             this.tabPanel.stripWrap.repaint();
41533         }
41534         this.fireEvent("activate", this.tabPanel, this);
41535     },
41536
41537     /**
41538      * Returns true if this tab is the active tab.
41539      * @return {Boolean}
41540      */
41541     isActive : function(){
41542         return this.tabPanel.getActiveTab() == this;
41543     },
41544
41545     /**
41546      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41547      */
41548     hide : function(){
41549         this.status_node.removeClass("active");
41550         this.hideAction();
41551         this.fireEvent("deactivate", this.tabPanel, this);
41552     },
41553
41554     hideAction : function(){
41555         this.bodyEl.hide();
41556         this.bodyEl.setStyle("position", "absolute");
41557         this.bodyEl.setLeft("-20000px");
41558         this.bodyEl.setTop("-20000px");
41559     },
41560
41561     showAction : function(){
41562         this.bodyEl.setStyle("position", "relative");
41563         this.bodyEl.setTop("");
41564         this.bodyEl.setLeft("");
41565         this.bodyEl.show();
41566     },
41567
41568     /**
41569      * Set the tooltip for the tab.
41570      * @param {String} tooltip The tab's tooltip
41571      */
41572     setTooltip : function(text){
41573         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41574             this.textEl.dom.qtip = text;
41575             this.textEl.dom.removeAttribute('title');
41576         }else{
41577             this.textEl.dom.title = text;
41578         }
41579     },
41580
41581     onTabClick : function(e){
41582         e.preventDefault();
41583         this.tabPanel.activate(this.id);
41584     },
41585
41586     onTabMouseDown : function(e){
41587         e.preventDefault();
41588         this.tabPanel.activate(this.id);
41589     },
41590 /*
41591     getWidth : function(){
41592         return this.inner.getWidth();
41593     },
41594
41595     setWidth : function(width){
41596         var iwidth = width - this.linode.getPadding("lr");
41597         this.inner.setWidth(iwidth);
41598         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41599         this.linode.setWidth(width);
41600     },
41601 */
41602     /**
41603      * Show or hide the tab
41604      * @param {Boolean} hidden True to hide or false to show.
41605      */
41606     setHidden : function(hidden){
41607         this.hidden = hidden;
41608         this.linode.setStyle("display", hidden ? "none" : "");
41609     },
41610
41611     /**
41612      * Returns true if this tab is "hidden"
41613      * @return {Boolean}
41614      */
41615     isHidden : function(){
41616         return this.hidden;
41617     },
41618
41619     /**
41620      * Returns the text for this tab
41621      * @return {String}
41622      */
41623     getText : function(){
41624         return this.text;
41625     },
41626     /*
41627     autoSize : function(){
41628         //this.el.beginMeasure();
41629         this.textEl.setWidth(1);
41630         /*
41631          *  #2804 [new] Tabs in Roojs
41632          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41633          */
41634         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41635         //this.el.endMeasure();
41636     //},
41637
41638     /**
41639      * Sets the text for the tab (Note: this also sets the tooltip text)
41640      * @param {String} text The tab's text and tooltip
41641      */
41642     setText : function(text){
41643         this.text = text;
41644         this.textEl.update(text);
41645         this.setTooltip(text);
41646         //if(!this.tabPanel.resizeTabs){
41647         //    this.autoSize();
41648         //}
41649     },
41650     /**
41651      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41652      */
41653     activate : function(){
41654         this.tabPanel.activate(this.id);
41655     },
41656
41657     /**
41658      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41659      */
41660     disable : function(){
41661         if(this.tabPanel.active != this){
41662             this.disabled = true;
41663             this.status_node.addClass("disabled");
41664         }
41665     },
41666
41667     /**
41668      * Enables this TabPanelItem if it was previously disabled.
41669      */
41670     enable : function(){
41671         this.disabled = false;
41672         this.status_node.removeClass("disabled");
41673     },
41674
41675     /**
41676      * Sets the content for this TabPanelItem.
41677      * @param {String} content The content
41678      * @param {Boolean} loadScripts true to look for and load scripts
41679      */
41680     setContent : function(content, loadScripts){
41681         this.bodyEl.update(content, loadScripts);
41682     },
41683
41684     /**
41685      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41686      * @return {Roo.UpdateManager} The UpdateManager
41687      */
41688     getUpdateManager : function(){
41689         return this.bodyEl.getUpdateManager();
41690     },
41691
41692     /**
41693      * Set a URL to be used to load the content for this TabPanelItem.
41694      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41695      * @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)
41696      * @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)
41697      * @return {Roo.UpdateManager} The UpdateManager
41698      */
41699     setUrl : function(url, params, loadOnce){
41700         if(this.refreshDelegate){
41701             this.un('activate', this.refreshDelegate);
41702         }
41703         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41704         this.on("activate", this.refreshDelegate);
41705         return this.bodyEl.getUpdateManager();
41706     },
41707
41708     /** @private */
41709     _handleRefresh : function(url, params, loadOnce){
41710         if(!loadOnce || !this.loaded){
41711             var updater = this.bodyEl.getUpdateManager();
41712             updater.update(url, params, this._setLoaded.createDelegate(this));
41713         }
41714     },
41715
41716     /**
41717      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41718      *   Will fail silently if the setUrl method has not been called.
41719      *   This does not activate the panel, just updates its content.
41720      */
41721     refresh : function(){
41722         if(this.refreshDelegate){
41723            this.loaded = false;
41724            this.refreshDelegate();
41725         }
41726     },
41727
41728     /** @private */
41729     _setLoaded : function(){
41730         this.loaded = true;
41731     },
41732
41733     /** @private */
41734     closeClick : function(e){
41735         var o = {};
41736         e.stopEvent();
41737         this.fireEvent("beforeclose", this, o);
41738         if(o.cancel !== true){
41739             this.tabPanel.removeTab(this.id);
41740         }
41741     },
41742     /**
41743      * The text displayed in the tooltip for the close icon.
41744      * @type String
41745      */
41746     closeText : "Close this tab"
41747 });
41748 /**
41749 *    This script refer to:
41750 *    Title: International Telephone Input
41751 *    Author: Jack O'Connor
41752 *    Code version:  v12.1.12
41753 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41754 **/
41755
41756 Roo.bootstrap.PhoneInputData = function() {
41757     var d = [
41758       [
41759         "Afghanistan (‫افغانستان‬‎)",
41760         "af",
41761         "93"
41762       ],
41763       [
41764         "Albania (Shqipëri)",
41765         "al",
41766         "355"
41767       ],
41768       [
41769         "Algeria (‫الجزائر‬‎)",
41770         "dz",
41771         "213"
41772       ],
41773       [
41774         "American Samoa",
41775         "as",
41776         "1684"
41777       ],
41778       [
41779         "Andorra",
41780         "ad",
41781         "376"
41782       ],
41783       [
41784         "Angola",
41785         "ao",
41786         "244"
41787       ],
41788       [
41789         "Anguilla",
41790         "ai",
41791         "1264"
41792       ],
41793       [
41794         "Antigua and Barbuda",
41795         "ag",
41796         "1268"
41797       ],
41798       [
41799         "Argentina",
41800         "ar",
41801         "54"
41802       ],
41803       [
41804         "Armenia (Հայաստան)",
41805         "am",
41806         "374"
41807       ],
41808       [
41809         "Aruba",
41810         "aw",
41811         "297"
41812       ],
41813       [
41814         "Australia",
41815         "au",
41816         "61",
41817         0
41818       ],
41819       [
41820         "Austria (Österreich)",
41821         "at",
41822         "43"
41823       ],
41824       [
41825         "Azerbaijan (Azərbaycan)",
41826         "az",
41827         "994"
41828       ],
41829       [
41830         "Bahamas",
41831         "bs",
41832         "1242"
41833       ],
41834       [
41835         "Bahrain (‫البحرين‬‎)",
41836         "bh",
41837         "973"
41838       ],
41839       [
41840         "Bangladesh (বাংলাদেশ)",
41841         "bd",
41842         "880"
41843       ],
41844       [
41845         "Barbados",
41846         "bb",
41847         "1246"
41848       ],
41849       [
41850         "Belarus (Беларусь)",
41851         "by",
41852         "375"
41853       ],
41854       [
41855         "Belgium (België)",
41856         "be",
41857         "32"
41858       ],
41859       [
41860         "Belize",
41861         "bz",
41862         "501"
41863       ],
41864       [
41865         "Benin (Bénin)",
41866         "bj",
41867         "229"
41868       ],
41869       [
41870         "Bermuda",
41871         "bm",
41872         "1441"
41873       ],
41874       [
41875         "Bhutan (འབྲུག)",
41876         "bt",
41877         "975"
41878       ],
41879       [
41880         "Bolivia",
41881         "bo",
41882         "591"
41883       ],
41884       [
41885         "Bosnia and Herzegovina (Босна и Херцеговина)",
41886         "ba",
41887         "387"
41888       ],
41889       [
41890         "Botswana",
41891         "bw",
41892         "267"
41893       ],
41894       [
41895         "Brazil (Brasil)",
41896         "br",
41897         "55"
41898       ],
41899       [
41900         "British Indian Ocean Territory",
41901         "io",
41902         "246"
41903       ],
41904       [
41905         "British Virgin Islands",
41906         "vg",
41907         "1284"
41908       ],
41909       [
41910         "Brunei",
41911         "bn",
41912         "673"
41913       ],
41914       [
41915         "Bulgaria (България)",
41916         "bg",
41917         "359"
41918       ],
41919       [
41920         "Burkina Faso",
41921         "bf",
41922         "226"
41923       ],
41924       [
41925         "Burundi (Uburundi)",
41926         "bi",
41927         "257"
41928       ],
41929       [
41930         "Cambodia (កម្ពុជា)",
41931         "kh",
41932         "855"
41933       ],
41934       [
41935         "Cameroon (Cameroun)",
41936         "cm",
41937         "237"
41938       ],
41939       [
41940         "Canada",
41941         "ca",
41942         "1",
41943         1,
41944         ["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"]
41945       ],
41946       [
41947         "Cape Verde (Kabu Verdi)",
41948         "cv",
41949         "238"
41950       ],
41951       [
41952         "Caribbean Netherlands",
41953         "bq",
41954         "599",
41955         1
41956       ],
41957       [
41958         "Cayman Islands",
41959         "ky",
41960         "1345"
41961       ],
41962       [
41963         "Central African Republic (République centrafricaine)",
41964         "cf",
41965         "236"
41966       ],
41967       [
41968         "Chad (Tchad)",
41969         "td",
41970         "235"
41971       ],
41972       [
41973         "Chile",
41974         "cl",
41975         "56"
41976       ],
41977       [
41978         "China (中国)",
41979         "cn",
41980         "86"
41981       ],
41982       [
41983         "Christmas Island",
41984         "cx",
41985         "61",
41986         2
41987       ],
41988       [
41989         "Cocos (Keeling) Islands",
41990         "cc",
41991         "61",
41992         1
41993       ],
41994       [
41995         "Colombia",
41996         "co",
41997         "57"
41998       ],
41999       [
42000         "Comoros (‫جزر القمر‬‎)",
42001         "km",
42002         "269"
42003       ],
42004       [
42005         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42006         "cd",
42007         "243"
42008       ],
42009       [
42010         "Congo (Republic) (Congo-Brazzaville)",
42011         "cg",
42012         "242"
42013       ],
42014       [
42015         "Cook Islands",
42016         "ck",
42017         "682"
42018       ],
42019       [
42020         "Costa Rica",
42021         "cr",
42022         "506"
42023       ],
42024       [
42025         "Côte d’Ivoire",
42026         "ci",
42027         "225"
42028       ],
42029       [
42030         "Croatia (Hrvatska)",
42031         "hr",
42032         "385"
42033       ],
42034       [
42035         "Cuba",
42036         "cu",
42037         "53"
42038       ],
42039       [
42040         "Curaçao",
42041         "cw",
42042         "599",
42043         0
42044       ],
42045       [
42046         "Cyprus (Κύπρος)",
42047         "cy",
42048         "357"
42049       ],
42050       [
42051         "Czech Republic (Česká republika)",
42052         "cz",
42053         "420"
42054       ],
42055       [
42056         "Denmark (Danmark)",
42057         "dk",
42058         "45"
42059       ],
42060       [
42061         "Djibouti",
42062         "dj",
42063         "253"
42064       ],
42065       [
42066         "Dominica",
42067         "dm",
42068         "1767"
42069       ],
42070       [
42071         "Dominican Republic (República Dominicana)",
42072         "do",
42073         "1",
42074         2,
42075         ["809", "829", "849"]
42076       ],
42077       [
42078         "Ecuador",
42079         "ec",
42080         "593"
42081       ],
42082       [
42083         "Egypt (‫مصر‬‎)",
42084         "eg",
42085         "20"
42086       ],
42087       [
42088         "El Salvador",
42089         "sv",
42090         "503"
42091       ],
42092       [
42093         "Equatorial Guinea (Guinea Ecuatorial)",
42094         "gq",
42095         "240"
42096       ],
42097       [
42098         "Eritrea",
42099         "er",
42100         "291"
42101       ],
42102       [
42103         "Estonia (Eesti)",
42104         "ee",
42105         "372"
42106       ],
42107       [
42108         "Ethiopia",
42109         "et",
42110         "251"
42111       ],
42112       [
42113         "Falkland Islands (Islas Malvinas)",
42114         "fk",
42115         "500"
42116       ],
42117       [
42118         "Faroe Islands (Føroyar)",
42119         "fo",
42120         "298"
42121       ],
42122       [
42123         "Fiji",
42124         "fj",
42125         "679"
42126       ],
42127       [
42128         "Finland (Suomi)",
42129         "fi",
42130         "358",
42131         0
42132       ],
42133       [
42134         "France",
42135         "fr",
42136         "33"
42137       ],
42138       [
42139         "French Guiana (Guyane française)",
42140         "gf",
42141         "594"
42142       ],
42143       [
42144         "French Polynesia (Polynésie française)",
42145         "pf",
42146         "689"
42147       ],
42148       [
42149         "Gabon",
42150         "ga",
42151         "241"
42152       ],
42153       [
42154         "Gambia",
42155         "gm",
42156         "220"
42157       ],
42158       [
42159         "Georgia (საქართველო)",
42160         "ge",
42161         "995"
42162       ],
42163       [
42164         "Germany (Deutschland)",
42165         "de",
42166         "49"
42167       ],
42168       [
42169         "Ghana (Gaana)",
42170         "gh",
42171         "233"
42172       ],
42173       [
42174         "Gibraltar",
42175         "gi",
42176         "350"
42177       ],
42178       [
42179         "Greece (Ελλάδα)",
42180         "gr",
42181         "30"
42182       ],
42183       [
42184         "Greenland (Kalaallit Nunaat)",
42185         "gl",
42186         "299"
42187       ],
42188       [
42189         "Grenada",
42190         "gd",
42191         "1473"
42192       ],
42193       [
42194         "Guadeloupe",
42195         "gp",
42196         "590",
42197         0
42198       ],
42199       [
42200         "Guam",
42201         "gu",
42202         "1671"
42203       ],
42204       [
42205         "Guatemala",
42206         "gt",
42207         "502"
42208       ],
42209       [
42210         "Guernsey",
42211         "gg",
42212         "44",
42213         1
42214       ],
42215       [
42216         "Guinea (Guinée)",
42217         "gn",
42218         "224"
42219       ],
42220       [
42221         "Guinea-Bissau (Guiné Bissau)",
42222         "gw",
42223         "245"
42224       ],
42225       [
42226         "Guyana",
42227         "gy",
42228         "592"
42229       ],
42230       [
42231         "Haiti",
42232         "ht",
42233         "509"
42234       ],
42235       [
42236         "Honduras",
42237         "hn",
42238         "504"
42239       ],
42240       [
42241         "Hong Kong (香港)",
42242         "hk",
42243         "852"
42244       ],
42245       [
42246         "Hungary (Magyarország)",
42247         "hu",
42248         "36"
42249       ],
42250       [
42251         "Iceland (Ísland)",
42252         "is",
42253         "354"
42254       ],
42255       [
42256         "India (भारत)",
42257         "in",
42258         "91"
42259       ],
42260       [
42261         "Indonesia",
42262         "id",
42263         "62"
42264       ],
42265       [
42266         "Iran (‫ایران‬‎)",
42267         "ir",
42268         "98"
42269       ],
42270       [
42271         "Iraq (‫العراق‬‎)",
42272         "iq",
42273         "964"
42274       ],
42275       [
42276         "Ireland",
42277         "ie",
42278         "353"
42279       ],
42280       [
42281         "Isle of Man",
42282         "im",
42283         "44",
42284         2
42285       ],
42286       [
42287         "Israel (‫ישראל‬‎)",
42288         "il",
42289         "972"
42290       ],
42291       [
42292         "Italy (Italia)",
42293         "it",
42294         "39",
42295         0
42296       ],
42297       [
42298         "Jamaica",
42299         "jm",
42300         "1876"
42301       ],
42302       [
42303         "Japan (日本)",
42304         "jp",
42305         "81"
42306       ],
42307       [
42308         "Jersey",
42309         "je",
42310         "44",
42311         3
42312       ],
42313       [
42314         "Jordan (‫الأردن‬‎)",
42315         "jo",
42316         "962"
42317       ],
42318       [
42319         "Kazakhstan (Казахстан)",
42320         "kz",
42321         "7",
42322         1
42323       ],
42324       [
42325         "Kenya",
42326         "ke",
42327         "254"
42328       ],
42329       [
42330         "Kiribati",
42331         "ki",
42332         "686"
42333       ],
42334       [
42335         "Kosovo",
42336         "xk",
42337         "383"
42338       ],
42339       [
42340         "Kuwait (‫الكويت‬‎)",
42341         "kw",
42342         "965"
42343       ],
42344       [
42345         "Kyrgyzstan (Кыргызстан)",
42346         "kg",
42347         "996"
42348       ],
42349       [
42350         "Laos (ລາວ)",
42351         "la",
42352         "856"
42353       ],
42354       [
42355         "Latvia (Latvija)",
42356         "lv",
42357         "371"
42358       ],
42359       [
42360         "Lebanon (‫لبنان‬‎)",
42361         "lb",
42362         "961"
42363       ],
42364       [
42365         "Lesotho",
42366         "ls",
42367         "266"
42368       ],
42369       [
42370         "Liberia",
42371         "lr",
42372         "231"
42373       ],
42374       [
42375         "Libya (‫ليبيا‬‎)",
42376         "ly",
42377         "218"
42378       ],
42379       [
42380         "Liechtenstein",
42381         "li",
42382         "423"
42383       ],
42384       [
42385         "Lithuania (Lietuva)",
42386         "lt",
42387         "370"
42388       ],
42389       [
42390         "Luxembourg",
42391         "lu",
42392         "352"
42393       ],
42394       [
42395         "Macau (澳門)",
42396         "mo",
42397         "853"
42398       ],
42399       [
42400         "Macedonia (FYROM) (Македонија)",
42401         "mk",
42402         "389"
42403       ],
42404       [
42405         "Madagascar (Madagasikara)",
42406         "mg",
42407         "261"
42408       ],
42409       [
42410         "Malawi",
42411         "mw",
42412         "265"
42413       ],
42414       [
42415         "Malaysia",
42416         "my",
42417         "60"
42418       ],
42419       [
42420         "Maldives",
42421         "mv",
42422         "960"
42423       ],
42424       [
42425         "Mali",
42426         "ml",
42427         "223"
42428       ],
42429       [
42430         "Malta",
42431         "mt",
42432         "356"
42433       ],
42434       [
42435         "Marshall Islands",
42436         "mh",
42437         "692"
42438       ],
42439       [
42440         "Martinique",
42441         "mq",
42442         "596"
42443       ],
42444       [
42445         "Mauritania (‫موريتانيا‬‎)",
42446         "mr",
42447         "222"
42448       ],
42449       [
42450         "Mauritius (Moris)",
42451         "mu",
42452         "230"
42453       ],
42454       [
42455         "Mayotte",
42456         "yt",
42457         "262",
42458         1
42459       ],
42460       [
42461         "Mexico (México)",
42462         "mx",
42463         "52"
42464       ],
42465       [
42466         "Micronesia",
42467         "fm",
42468         "691"
42469       ],
42470       [
42471         "Moldova (Republica Moldova)",
42472         "md",
42473         "373"
42474       ],
42475       [
42476         "Monaco",
42477         "mc",
42478         "377"
42479       ],
42480       [
42481         "Mongolia (Монгол)",
42482         "mn",
42483         "976"
42484       ],
42485       [
42486         "Montenegro (Crna Gora)",
42487         "me",
42488         "382"
42489       ],
42490       [
42491         "Montserrat",
42492         "ms",
42493         "1664"
42494       ],
42495       [
42496         "Morocco (‫المغرب‬‎)",
42497         "ma",
42498         "212",
42499         0
42500       ],
42501       [
42502         "Mozambique (Moçambique)",
42503         "mz",
42504         "258"
42505       ],
42506       [
42507         "Myanmar (Burma) (မြန်မာ)",
42508         "mm",
42509         "95"
42510       ],
42511       [
42512         "Namibia (Namibië)",
42513         "na",
42514         "264"
42515       ],
42516       [
42517         "Nauru",
42518         "nr",
42519         "674"
42520       ],
42521       [
42522         "Nepal (नेपाल)",
42523         "np",
42524         "977"
42525       ],
42526       [
42527         "Netherlands (Nederland)",
42528         "nl",
42529         "31"
42530       ],
42531       [
42532         "New Caledonia (Nouvelle-Calédonie)",
42533         "nc",
42534         "687"
42535       ],
42536       [
42537         "New Zealand",
42538         "nz",
42539         "64"
42540       ],
42541       [
42542         "Nicaragua",
42543         "ni",
42544         "505"
42545       ],
42546       [
42547         "Niger (Nijar)",
42548         "ne",
42549         "227"
42550       ],
42551       [
42552         "Nigeria",
42553         "ng",
42554         "234"
42555       ],
42556       [
42557         "Niue",
42558         "nu",
42559         "683"
42560       ],
42561       [
42562         "Norfolk Island",
42563         "nf",
42564         "672"
42565       ],
42566       [
42567         "North Korea (조선 민주주의 인민 공화국)",
42568         "kp",
42569         "850"
42570       ],
42571       [
42572         "Northern Mariana Islands",
42573         "mp",
42574         "1670"
42575       ],
42576       [
42577         "Norway (Norge)",
42578         "no",
42579         "47",
42580         0
42581       ],
42582       [
42583         "Oman (‫عُمان‬‎)",
42584         "om",
42585         "968"
42586       ],
42587       [
42588         "Pakistan (‫پاکستان‬‎)",
42589         "pk",
42590         "92"
42591       ],
42592       [
42593         "Palau",
42594         "pw",
42595         "680"
42596       ],
42597       [
42598         "Palestine (‫فلسطين‬‎)",
42599         "ps",
42600         "970"
42601       ],
42602       [
42603         "Panama (Panamá)",
42604         "pa",
42605         "507"
42606       ],
42607       [
42608         "Papua New Guinea",
42609         "pg",
42610         "675"
42611       ],
42612       [
42613         "Paraguay",
42614         "py",
42615         "595"
42616       ],
42617       [
42618         "Peru (Perú)",
42619         "pe",
42620         "51"
42621       ],
42622       [
42623         "Philippines",
42624         "ph",
42625         "63"
42626       ],
42627       [
42628         "Poland (Polska)",
42629         "pl",
42630         "48"
42631       ],
42632       [
42633         "Portugal",
42634         "pt",
42635         "351"
42636       ],
42637       [
42638         "Puerto Rico",
42639         "pr",
42640         "1",
42641         3,
42642         ["787", "939"]
42643       ],
42644       [
42645         "Qatar (‫قطر‬‎)",
42646         "qa",
42647         "974"
42648       ],
42649       [
42650         "Réunion (La Réunion)",
42651         "re",
42652         "262",
42653         0
42654       ],
42655       [
42656         "Romania (România)",
42657         "ro",
42658         "40"
42659       ],
42660       [
42661         "Russia (Россия)",
42662         "ru",
42663         "7",
42664         0
42665       ],
42666       [
42667         "Rwanda",
42668         "rw",
42669         "250"
42670       ],
42671       [
42672         "Saint Barthélemy",
42673         "bl",
42674         "590",
42675         1
42676       ],
42677       [
42678         "Saint Helena",
42679         "sh",
42680         "290"
42681       ],
42682       [
42683         "Saint Kitts and Nevis",
42684         "kn",
42685         "1869"
42686       ],
42687       [
42688         "Saint Lucia",
42689         "lc",
42690         "1758"
42691       ],
42692       [
42693         "Saint Martin (Saint-Martin (partie française))",
42694         "mf",
42695         "590",
42696         2
42697       ],
42698       [
42699         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42700         "pm",
42701         "508"
42702       ],
42703       [
42704         "Saint Vincent and the Grenadines",
42705         "vc",
42706         "1784"
42707       ],
42708       [
42709         "Samoa",
42710         "ws",
42711         "685"
42712       ],
42713       [
42714         "San Marino",
42715         "sm",
42716         "378"
42717       ],
42718       [
42719         "São Tomé and Príncipe (São Tomé e Príncipe)",
42720         "st",
42721         "239"
42722       ],
42723       [
42724         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42725         "sa",
42726         "966"
42727       ],
42728       [
42729         "Senegal (Sénégal)",
42730         "sn",
42731         "221"
42732       ],
42733       [
42734         "Serbia (Србија)",
42735         "rs",
42736         "381"
42737       ],
42738       [
42739         "Seychelles",
42740         "sc",
42741         "248"
42742       ],
42743       [
42744         "Sierra Leone",
42745         "sl",
42746         "232"
42747       ],
42748       [
42749         "Singapore",
42750         "sg",
42751         "65"
42752       ],
42753       [
42754         "Sint Maarten",
42755         "sx",
42756         "1721"
42757       ],
42758       [
42759         "Slovakia (Slovensko)",
42760         "sk",
42761         "421"
42762       ],
42763       [
42764         "Slovenia (Slovenija)",
42765         "si",
42766         "386"
42767       ],
42768       [
42769         "Solomon Islands",
42770         "sb",
42771         "677"
42772       ],
42773       [
42774         "Somalia (Soomaaliya)",
42775         "so",
42776         "252"
42777       ],
42778       [
42779         "South Africa",
42780         "za",
42781         "27"
42782       ],
42783       [
42784         "South Korea (대한민국)",
42785         "kr",
42786         "82"
42787       ],
42788       [
42789         "South Sudan (‫جنوب السودان‬‎)",
42790         "ss",
42791         "211"
42792       ],
42793       [
42794         "Spain (España)",
42795         "es",
42796         "34"
42797       ],
42798       [
42799         "Sri Lanka (ශ්‍රී ලංකාව)",
42800         "lk",
42801         "94"
42802       ],
42803       [
42804         "Sudan (‫السودان‬‎)",
42805         "sd",
42806         "249"
42807       ],
42808       [
42809         "Suriname",
42810         "sr",
42811         "597"
42812       ],
42813       [
42814         "Svalbard and Jan Mayen",
42815         "sj",
42816         "47",
42817         1
42818       ],
42819       [
42820         "Swaziland",
42821         "sz",
42822         "268"
42823       ],
42824       [
42825         "Sweden (Sverige)",
42826         "se",
42827         "46"
42828       ],
42829       [
42830         "Switzerland (Schweiz)",
42831         "ch",
42832         "41"
42833       ],
42834       [
42835         "Syria (‫سوريا‬‎)",
42836         "sy",
42837         "963"
42838       ],
42839       [
42840         "Taiwan (台灣)",
42841         "tw",
42842         "886"
42843       ],
42844       [
42845         "Tajikistan",
42846         "tj",
42847         "992"
42848       ],
42849       [
42850         "Tanzania",
42851         "tz",
42852         "255"
42853       ],
42854       [
42855         "Thailand (ไทย)",
42856         "th",
42857         "66"
42858       ],
42859       [
42860         "Timor-Leste",
42861         "tl",
42862         "670"
42863       ],
42864       [
42865         "Togo",
42866         "tg",
42867         "228"
42868       ],
42869       [
42870         "Tokelau",
42871         "tk",
42872         "690"
42873       ],
42874       [
42875         "Tonga",
42876         "to",
42877         "676"
42878       ],
42879       [
42880         "Trinidad and Tobago",
42881         "tt",
42882         "1868"
42883       ],
42884       [
42885         "Tunisia (‫تونس‬‎)",
42886         "tn",
42887         "216"
42888       ],
42889       [
42890         "Turkey (Türkiye)",
42891         "tr",
42892         "90"
42893       ],
42894       [
42895         "Turkmenistan",
42896         "tm",
42897         "993"
42898       ],
42899       [
42900         "Turks and Caicos Islands",
42901         "tc",
42902         "1649"
42903       ],
42904       [
42905         "Tuvalu",
42906         "tv",
42907         "688"
42908       ],
42909       [
42910         "U.S. Virgin Islands",
42911         "vi",
42912         "1340"
42913       ],
42914       [
42915         "Uganda",
42916         "ug",
42917         "256"
42918       ],
42919       [
42920         "Ukraine (Україна)",
42921         "ua",
42922         "380"
42923       ],
42924       [
42925         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42926         "ae",
42927         "971"
42928       ],
42929       [
42930         "United Kingdom",
42931         "gb",
42932         "44",
42933         0
42934       ],
42935       [
42936         "United States",
42937         "us",
42938         "1",
42939         0
42940       ],
42941       [
42942         "Uruguay",
42943         "uy",
42944         "598"
42945       ],
42946       [
42947         "Uzbekistan (Oʻzbekiston)",
42948         "uz",
42949         "998"
42950       ],
42951       [
42952         "Vanuatu",
42953         "vu",
42954         "678"
42955       ],
42956       [
42957         "Vatican City (Città del Vaticano)",
42958         "va",
42959         "39",
42960         1
42961       ],
42962       [
42963         "Venezuela",
42964         "ve",
42965         "58"
42966       ],
42967       [
42968         "Vietnam (Việt Nam)",
42969         "vn",
42970         "84"
42971       ],
42972       [
42973         "Wallis and Futuna (Wallis-et-Futuna)",
42974         "wf",
42975         "681"
42976       ],
42977       [
42978         "Western Sahara (‫الصحراء الغربية‬‎)",
42979         "eh",
42980         "212",
42981         1
42982       ],
42983       [
42984         "Yemen (‫اليمن‬‎)",
42985         "ye",
42986         "967"
42987       ],
42988       [
42989         "Zambia",
42990         "zm",
42991         "260"
42992       ],
42993       [
42994         "Zimbabwe",
42995         "zw",
42996         "263"
42997       ],
42998       [
42999         "Åland Islands",
43000         "ax",
43001         "358",
43002         1
43003       ]
43004   ];
43005   
43006   return d;
43007 }/**
43008 *    This script refer to:
43009 *    Title: International Telephone Input
43010 *    Author: Jack O'Connor
43011 *    Code version:  v12.1.12
43012 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43013 **/
43014
43015 /**
43016  * @class Roo.bootstrap.PhoneInput
43017  * @extends Roo.bootstrap.TriggerField
43018  * An input with International dial-code selection
43019  
43020  * @cfg {String} defaultDialCode default '+852'
43021  * @cfg {Array} preferedCountries default []
43022   
43023  * @constructor
43024  * Create a new PhoneInput.
43025  * @param {Object} config Configuration options
43026  */
43027
43028 Roo.bootstrap.PhoneInput = function(config) {
43029     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43030 };
43031
43032 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43033         
43034         listWidth: undefined,
43035         
43036         selectedClass: 'active',
43037         
43038         invalidClass : "has-warning",
43039         
43040         validClass: 'has-success',
43041         
43042         allowed: '0123456789',
43043         
43044         max_length: 15,
43045         
43046         /**
43047          * @cfg {String} defaultDialCode The default dial code when initializing the input
43048          */
43049         defaultDialCode: '+852',
43050         
43051         /**
43052          * @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
43053          */
43054         preferedCountries: false,
43055         
43056         getAutoCreate : function()
43057         {
43058             var data = Roo.bootstrap.PhoneInputData();
43059             var align = this.labelAlign || this.parentLabelAlign();
43060             var id = Roo.id();
43061             
43062             this.allCountries = [];
43063             this.dialCodeMapping = [];
43064             
43065             for (var i = 0; i < data.length; i++) {
43066               var c = data[i];
43067               this.allCountries[i] = {
43068                 name: c[0],
43069                 iso2: c[1],
43070                 dialCode: c[2],
43071                 priority: c[3] || 0,
43072                 areaCodes: c[4] || null
43073               };
43074               this.dialCodeMapping[c[2]] = {
43075                   name: c[0],
43076                   iso2: c[1],
43077                   priority: c[3] || 0,
43078                   areaCodes: c[4] || null
43079               };
43080             }
43081             
43082             var cfg = {
43083                 cls: 'form-group',
43084                 cn: []
43085             };
43086             
43087             var input =  {
43088                 tag: 'input',
43089                 id : id,
43090                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43091                 maxlength: this.max_length,
43092                 cls : 'form-control tel-input',
43093                 autocomplete: 'new-password'
43094             };
43095             
43096             var hiddenInput = {
43097                 tag: 'input',
43098                 type: 'hidden',
43099                 cls: 'hidden-tel-input'
43100             };
43101             
43102             if (this.name) {
43103                 hiddenInput.name = this.name;
43104             }
43105             
43106             if (this.disabled) {
43107                 input.disabled = true;
43108             }
43109             
43110             var flag_container = {
43111                 tag: 'div',
43112                 cls: 'flag-box',
43113                 cn: [
43114                     {
43115                         tag: 'div',
43116                         cls: 'flag'
43117                     },
43118                     {
43119                         tag: 'div',
43120                         cls: 'caret'
43121                     }
43122                 ]
43123             };
43124             
43125             var box = {
43126                 tag: 'div',
43127                 cls: this.hasFeedback ? 'has-feedback' : '',
43128                 cn: [
43129                     hiddenInput,
43130                     input,
43131                     {
43132                         tag: 'input',
43133                         cls: 'dial-code-holder',
43134                         disabled: true
43135                     }
43136                 ]
43137             };
43138             
43139             var container = {
43140                 cls: 'roo-select2-container input-group',
43141                 cn: [
43142                     flag_container,
43143                     box
43144                 ]
43145             };
43146             
43147             if (this.fieldLabel.length) {
43148                 var indicator = {
43149                     tag: 'i',
43150                     tooltip: 'This field is required'
43151                 };
43152                 
43153                 var label = {
43154                     tag: 'label',
43155                     'for':  id,
43156                     cls: 'control-label',
43157                     cn: []
43158                 };
43159                 
43160                 var label_text = {
43161                     tag: 'span',
43162                     html: this.fieldLabel
43163                 };
43164                 
43165                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43166                 label.cn = [
43167                     indicator,
43168                     label_text
43169                 ];
43170                 
43171                 if(this.indicatorpos == 'right') {
43172                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43173                     label.cn = [
43174                         label_text,
43175                         indicator
43176                     ];
43177                 }
43178                 
43179                 if(align == 'left') {
43180                     container = {
43181                         tag: 'div',
43182                         cn: [
43183                             container
43184                         ]
43185                     };
43186                     
43187                     if(this.labelWidth > 12){
43188                         label.style = "width: " + this.labelWidth + 'px';
43189                     }
43190                     if(this.labelWidth < 13 && this.labelmd == 0){
43191                         this.labelmd = this.labelWidth;
43192                     }
43193                     if(this.labellg > 0){
43194                         label.cls += ' col-lg-' + this.labellg;
43195                         input.cls += ' col-lg-' + (12 - this.labellg);
43196                     }
43197                     if(this.labelmd > 0){
43198                         label.cls += ' col-md-' + this.labelmd;
43199                         container.cls += ' col-md-' + (12 - this.labelmd);
43200                     }
43201                     if(this.labelsm > 0){
43202                         label.cls += ' col-sm-' + this.labelsm;
43203                         container.cls += ' col-sm-' + (12 - this.labelsm);
43204                     }
43205                     if(this.labelxs > 0){
43206                         label.cls += ' col-xs-' + this.labelxs;
43207                         container.cls += ' col-xs-' + (12 - this.labelxs);
43208                     }
43209                 }
43210             }
43211             
43212             cfg.cn = [
43213                 label,
43214                 container
43215             ];
43216             
43217             var settings = this;
43218             
43219             ['xs','sm','md','lg'].map(function(size){
43220                 if (settings[size]) {
43221                     cfg.cls += ' col-' + size + '-' + settings[size];
43222                 }
43223             });
43224             
43225             this.store = new Roo.data.Store({
43226                 proxy : new Roo.data.MemoryProxy({}),
43227                 reader : new Roo.data.JsonReader({
43228                     fields : [
43229                         {
43230                             'name' : 'name',
43231                             'type' : 'string'
43232                         },
43233                         {
43234                             'name' : 'iso2',
43235                             'type' : 'string'
43236                         },
43237                         {
43238                             'name' : 'dialCode',
43239                             'type' : 'string'
43240                         },
43241                         {
43242                             'name' : 'priority',
43243                             'type' : 'string'
43244                         },
43245                         {
43246                             'name' : 'areaCodes',
43247                             'type' : 'string'
43248                         }
43249                     ]
43250                 })
43251             });
43252             
43253             if(!this.preferedCountries) {
43254                 this.preferedCountries = [
43255                     'hk',
43256                     'gb',
43257                     'us'
43258                 ];
43259             }
43260             
43261             var p = this.preferedCountries.reverse();
43262             
43263             if(p) {
43264                 for (var i = 0; i < p.length; i++) {
43265                     for (var j = 0; j < this.allCountries.length; j++) {
43266                         if(this.allCountries[j].iso2 == p[i]) {
43267                             var t = this.allCountries[j];
43268                             this.allCountries.splice(j,1);
43269                             this.allCountries.unshift(t);
43270                         }
43271                     } 
43272                 }
43273             }
43274             
43275             this.store.proxy.data = {
43276                 success: true,
43277                 data: this.allCountries
43278             };
43279             
43280             return cfg;
43281         },
43282         
43283         initEvents : function()
43284         {
43285             this.createList();
43286             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43287             
43288             this.indicator = this.indicatorEl();
43289             this.flag = this.flagEl();
43290             this.dialCodeHolder = this.dialCodeHolderEl();
43291             
43292             this.trigger = this.el.select('div.flag-box',true).first();
43293             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43294             
43295             var _this = this;
43296             
43297             (function(){
43298                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43299                 _this.list.setWidth(lw);
43300             }).defer(100);
43301             
43302             this.list.on('mouseover', this.onViewOver, this);
43303             this.list.on('mousemove', this.onViewMove, this);
43304             this.inputEl().on("keyup", this.onKeyUp, this);
43305             this.inputEl().on("keypress", this.onKeyPress, this);
43306             
43307             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43308
43309             this.view = new Roo.View(this.list, this.tpl, {
43310                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43311             });
43312             
43313             this.view.on('click', this.onViewClick, this);
43314             this.setValue(this.defaultDialCode);
43315         },
43316         
43317         onTriggerClick : function(e)
43318         {
43319             Roo.log('trigger click');
43320             if(this.disabled){
43321                 return;
43322             }
43323             
43324             if(this.isExpanded()){
43325                 this.collapse();
43326                 this.hasFocus = false;
43327             }else {
43328                 this.store.load({});
43329                 this.hasFocus = true;
43330                 this.expand();
43331             }
43332         },
43333         
43334         isExpanded : function()
43335         {
43336             return this.list.isVisible();
43337         },
43338         
43339         collapse : function()
43340         {
43341             if(!this.isExpanded()){
43342                 return;
43343             }
43344             this.list.hide();
43345             Roo.get(document).un('mousedown', this.collapseIf, this);
43346             Roo.get(document).un('mousewheel', this.collapseIf, this);
43347             this.fireEvent('collapse', this);
43348             this.validate();
43349         },
43350         
43351         expand : function()
43352         {
43353             Roo.log('expand');
43354
43355             if(this.isExpanded() || !this.hasFocus){
43356                 return;
43357             }
43358             
43359             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43360             this.list.setWidth(lw);
43361             
43362             this.list.show();
43363             this.restrictHeight();
43364             
43365             Roo.get(document).on('mousedown', this.collapseIf, this);
43366             Roo.get(document).on('mousewheel', this.collapseIf, this);
43367             
43368             this.fireEvent('expand', this);
43369         },
43370         
43371         restrictHeight : function()
43372         {
43373             this.list.alignTo(this.inputEl(), this.listAlign);
43374             this.list.alignTo(this.inputEl(), this.listAlign);
43375         },
43376         
43377         onViewOver : function(e, t)
43378         {
43379             if(this.inKeyMode){
43380                 return;
43381             }
43382             var item = this.view.findItemFromChild(t);
43383             
43384             if(item){
43385                 var index = this.view.indexOf(item);
43386                 this.select(index, false);
43387             }
43388         },
43389
43390         // private
43391         onViewClick : function(view, doFocus, el, e)
43392         {
43393             var index = this.view.getSelectedIndexes()[0];
43394             
43395             var r = this.store.getAt(index);
43396             
43397             if(r){
43398                 this.onSelect(r, index);
43399             }
43400             if(doFocus !== false && !this.blockFocus){
43401                 this.inputEl().focus();
43402             }
43403         },
43404         
43405         onViewMove : function(e, t)
43406         {
43407             this.inKeyMode = false;
43408         },
43409         
43410         select : function(index, scrollIntoView)
43411         {
43412             this.selectedIndex = index;
43413             this.view.select(index);
43414             if(scrollIntoView !== false){
43415                 var el = this.view.getNode(index);
43416                 if(el){
43417                     this.list.scrollChildIntoView(el, false);
43418                 }
43419             }
43420         },
43421         
43422         createList : function()
43423         {
43424             this.list = Roo.get(document.body).createChild({
43425                 tag: 'ul',
43426                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43427                 style: 'display:none'
43428             });
43429             
43430             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43431         },
43432         
43433         collapseIf : function(e)
43434         {
43435             var in_combo  = e.within(this.el);
43436             var in_list =  e.within(this.list);
43437             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43438             
43439             if (in_combo || in_list || is_list) {
43440                 return;
43441             }
43442             this.collapse();
43443         },
43444         
43445         onSelect : function(record, index)
43446         {
43447             if(this.fireEvent('beforeselect', this, record, index) !== false){
43448                 
43449                 this.setFlagClass(record.data.iso2);
43450                 this.setDialCode(record.data.dialCode);
43451                 this.hasFocus = false;
43452                 this.collapse();
43453                 this.fireEvent('select', this, record, index);
43454             }
43455         },
43456         
43457         flagEl : function()
43458         {
43459             var flag = this.el.select('div.flag',true).first();
43460             if(!flag){
43461                 return false;
43462             }
43463             return flag;
43464         },
43465         
43466         dialCodeHolderEl : function()
43467         {
43468             var d = this.el.select('input.dial-code-holder',true).first();
43469             if(!d){
43470                 return false;
43471             }
43472             return d;
43473         },
43474         
43475         setDialCode : function(v)
43476         {
43477             this.dialCodeHolder.dom.value = '+'+v;
43478         },
43479         
43480         setFlagClass : function(n)
43481         {
43482             this.flag.dom.className = 'flag '+n;
43483         },
43484         
43485         getValue : function()
43486         {
43487             var v = this.inputEl().getValue();
43488             if(this.dialCodeHolder) {
43489                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43490             }
43491             return v;
43492         },
43493         
43494         setValue : function(v)
43495         {
43496             var d = this.getDialCode(v);
43497             
43498             //invalid dial code
43499             if(v.length == 0 || !d || d.length == 0) {
43500                 if(this.rendered){
43501                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43502                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43503                 }
43504                 return;
43505             }
43506             
43507             //valid dial code
43508             this.setFlagClass(this.dialCodeMapping[d].iso2);
43509             this.setDialCode(d);
43510             this.inputEl().dom.value = v.replace('+'+d,'');
43511             this.hiddenEl().dom.value = this.getValue();
43512             
43513             this.validate();
43514         },
43515         
43516         getDialCode : function(v)
43517         {
43518             v = v ||  '';
43519             
43520             if (v.length == 0) {
43521                 return this.dialCodeHolder.dom.value;
43522             }
43523             
43524             var dialCode = "";
43525             if (v.charAt(0) != "+") {
43526                 return false;
43527             }
43528             var numericChars = "";
43529             for (var i = 1; i < v.length; i++) {
43530               var c = v.charAt(i);
43531               if (!isNaN(c)) {
43532                 numericChars += c;
43533                 if (this.dialCodeMapping[numericChars]) {
43534                   dialCode = v.substr(1, i);
43535                 }
43536                 if (numericChars.length == 4) {
43537                   break;
43538                 }
43539               }
43540             }
43541             return dialCode;
43542         },
43543         
43544         reset : function()
43545         {
43546             this.setValue(this.defaultDialCode);
43547             this.validate();
43548         },
43549         
43550         hiddenEl : function()
43551         {
43552             return this.el.select('input.hidden-tel-input',true).first();
43553         },
43554         
43555         // after setting val
43556         onKeyUp : function(e){
43557             this.setValue(this.getValue());
43558         },
43559         
43560         onKeyPress : function(e){
43561             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43562                 e.stopEvent();
43563             }
43564         }
43565         
43566 });
43567 /**
43568  * @class Roo.bootstrap.MoneyField
43569  * @extends Roo.bootstrap.ComboBox
43570  * Bootstrap MoneyField class
43571  * 
43572  * @constructor
43573  * Create a new MoneyField.
43574  * @param {Object} config Configuration options
43575  */
43576
43577 Roo.bootstrap.MoneyField = function(config) {
43578     
43579     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43580     
43581 };
43582
43583 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43584     
43585     /**
43586      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43587      */
43588     allowDecimals : true,
43589     /**
43590      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43591      */
43592     decimalSeparator : ".",
43593     /**
43594      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43595      */
43596     decimalPrecision : 0,
43597     /**
43598      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43599      */
43600     allowNegative : true,
43601     /**
43602      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43603      */
43604     allowZero: true,
43605     /**
43606      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43607      */
43608     minValue : Number.NEGATIVE_INFINITY,
43609     /**
43610      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43611      */
43612     maxValue : Number.MAX_VALUE,
43613     /**
43614      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43615      */
43616     minText : "The minimum value for this field is {0}",
43617     /**
43618      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43619      */
43620     maxText : "The maximum value for this field is {0}",
43621     /**
43622      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43623      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43624      */
43625     nanText : "{0} is not a valid number",
43626     /**
43627      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43628      */
43629     castInt : true,
43630     /**
43631      * @cfg {String} defaults currency of the MoneyField
43632      * value should be in lkey
43633      */
43634     defaultCurrency : false,
43635     /**
43636      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43637      */
43638     thousandsDelimiter : false,
43639     /**
43640      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43641      */
43642     max_length: false,
43643     
43644     inputlg : 9,
43645     inputmd : 9,
43646     inputsm : 9,
43647     inputxs : 6,
43648     
43649     store : false,
43650     
43651     getAutoCreate : function()
43652     {
43653         var align = this.labelAlign || this.parentLabelAlign();
43654         
43655         var id = Roo.id();
43656
43657         var cfg = {
43658             cls: 'form-group',
43659             cn: []
43660         };
43661
43662         var input =  {
43663             tag: 'input',
43664             id : id,
43665             cls : 'form-control roo-money-amount-input',
43666             autocomplete: 'new-password'
43667         };
43668         
43669         var hiddenInput = {
43670             tag: 'input',
43671             type: 'hidden',
43672             id: Roo.id(),
43673             cls: 'hidden-number-input'
43674         };
43675         
43676         if(this.max_length) {
43677             input.maxlength = this.max_length; 
43678         }
43679         
43680         if (this.name) {
43681             hiddenInput.name = this.name;
43682         }
43683
43684         if (this.disabled) {
43685             input.disabled = true;
43686         }
43687
43688         var clg = 12 - this.inputlg;
43689         var cmd = 12 - this.inputmd;
43690         var csm = 12 - this.inputsm;
43691         var cxs = 12 - this.inputxs;
43692         
43693         var container = {
43694             tag : 'div',
43695             cls : 'row roo-money-field',
43696             cn : [
43697                 {
43698                     tag : 'div',
43699                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43700                     cn : [
43701                         {
43702                             tag : 'div',
43703                             cls: 'roo-select2-container input-group',
43704                             cn: [
43705                                 {
43706                                     tag : 'input',
43707                                     cls : 'form-control roo-money-currency-input',
43708                                     autocomplete: 'new-password',
43709                                     readOnly : 1,
43710                                     name : this.currencyName
43711                                 },
43712                                 {
43713                                     tag :'span',
43714                                     cls : 'input-group-addon',
43715                                     cn : [
43716                                         {
43717                                             tag: 'span',
43718                                             cls: 'caret'
43719                                         }
43720                                     ]
43721                                 }
43722                             ]
43723                         }
43724                     ]
43725                 },
43726                 {
43727                     tag : 'div',
43728                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43729                     cn : [
43730                         {
43731                             tag: 'div',
43732                             cls: this.hasFeedback ? 'has-feedback' : '',
43733                             cn: [
43734                                 input
43735                             ]
43736                         }
43737                     ]
43738                 }
43739             ]
43740             
43741         };
43742         
43743         if (this.fieldLabel.length) {
43744             var indicator = {
43745                 tag: 'i',
43746                 tooltip: 'This field is required'
43747             };
43748
43749             var label = {
43750                 tag: 'label',
43751                 'for':  id,
43752                 cls: 'control-label',
43753                 cn: []
43754             };
43755
43756             var label_text = {
43757                 tag: 'span',
43758                 html: this.fieldLabel
43759             };
43760
43761             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43762             label.cn = [
43763                 indicator,
43764                 label_text
43765             ];
43766
43767             if(this.indicatorpos == 'right') {
43768                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43769                 label.cn = [
43770                     label_text,
43771                     indicator
43772                 ];
43773             }
43774
43775             if(align == 'left') {
43776                 container = {
43777                     tag: 'div',
43778                     cn: [
43779                         container
43780                     ]
43781                 };
43782
43783                 if(this.labelWidth > 12){
43784                     label.style = "width: " + this.labelWidth + 'px';
43785                 }
43786                 if(this.labelWidth < 13 && this.labelmd == 0){
43787                     this.labelmd = this.labelWidth;
43788                 }
43789                 if(this.labellg > 0){
43790                     label.cls += ' col-lg-' + this.labellg;
43791                     input.cls += ' col-lg-' + (12 - this.labellg);
43792                 }
43793                 if(this.labelmd > 0){
43794                     label.cls += ' col-md-' + this.labelmd;
43795                     container.cls += ' col-md-' + (12 - this.labelmd);
43796                 }
43797                 if(this.labelsm > 0){
43798                     label.cls += ' col-sm-' + this.labelsm;
43799                     container.cls += ' col-sm-' + (12 - this.labelsm);
43800                 }
43801                 if(this.labelxs > 0){
43802                     label.cls += ' col-xs-' + this.labelxs;
43803                     container.cls += ' col-xs-' + (12 - this.labelxs);
43804                 }
43805             }
43806         }
43807
43808         cfg.cn = [
43809             label,
43810             container,
43811             hiddenInput
43812         ];
43813         
43814         var settings = this;
43815
43816         ['xs','sm','md','lg'].map(function(size){
43817             if (settings[size]) {
43818                 cfg.cls += ' col-' + size + '-' + settings[size];
43819             }
43820         });
43821         
43822         return cfg;
43823     },
43824     
43825     initEvents : function()
43826     {
43827         this.indicator = this.indicatorEl();
43828         
43829         this.initCurrencyEvent();
43830         
43831         this.initNumberEvent();
43832     },
43833     
43834     initCurrencyEvent : function()
43835     {
43836         if (!this.store) {
43837             throw "can not find store for combo";
43838         }
43839         
43840         this.store = Roo.factory(this.store, Roo.data);
43841         this.store.parent = this;
43842         
43843         this.createList();
43844         
43845         this.triggerEl = this.el.select('.input-group-addon', true).first();
43846         
43847         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43848         
43849         var _this = this;
43850         
43851         (function(){
43852             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43853             _this.list.setWidth(lw);
43854         }).defer(100);
43855         
43856         this.list.on('mouseover', this.onViewOver, this);
43857         this.list.on('mousemove', this.onViewMove, this);
43858         this.list.on('scroll', this.onViewScroll, this);
43859         
43860         if(!this.tpl){
43861             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43862         }
43863         
43864         this.view = new Roo.View(this.list, this.tpl, {
43865             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43866         });
43867         
43868         this.view.on('click', this.onViewClick, this);
43869         
43870         this.store.on('beforeload', this.onBeforeLoad, this);
43871         this.store.on('load', this.onLoad, this);
43872         this.store.on('loadexception', this.onLoadException, this);
43873         
43874         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43875             "up" : function(e){
43876                 this.inKeyMode = true;
43877                 this.selectPrev();
43878             },
43879
43880             "down" : function(e){
43881                 if(!this.isExpanded()){
43882                     this.onTriggerClick();
43883                 }else{
43884                     this.inKeyMode = true;
43885                     this.selectNext();
43886                 }
43887             },
43888
43889             "enter" : function(e){
43890                 this.collapse();
43891                 
43892                 if(this.fireEvent("specialkey", this, e)){
43893                     this.onViewClick(false);
43894                 }
43895                 
43896                 return true;
43897             },
43898
43899             "esc" : function(e){
43900                 this.collapse();
43901             },
43902
43903             "tab" : function(e){
43904                 this.collapse();
43905                 
43906                 if(this.fireEvent("specialkey", this, e)){
43907                     this.onViewClick(false);
43908                 }
43909                 
43910                 return true;
43911             },
43912
43913             scope : this,
43914
43915             doRelay : function(foo, bar, hname){
43916                 if(hname == 'down' || this.scope.isExpanded()){
43917                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43918                 }
43919                 return true;
43920             },
43921
43922             forceKeyDown: true
43923         });
43924         
43925         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43926         
43927     },
43928     
43929     initNumberEvent : function(e)
43930     {
43931         this.inputEl().on("keydown" , this.fireKey,  this);
43932         this.inputEl().on("focus", this.onFocus,  this);
43933         this.inputEl().on("blur", this.onBlur,  this);
43934         
43935         this.inputEl().relayEvent('keyup', this);
43936         
43937         if(this.indicator){
43938             this.indicator.addClass('invisible');
43939         }
43940  
43941         this.originalValue = this.getValue();
43942         
43943         if(this.validationEvent == 'keyup'){
43944             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43945             this.inputEl().on('keyup', this.filterValidation, this);
43946         }
43947         else if(this.validationEvent !== false){
43948             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43949         }
43950         
43951         if(this.selectOnFocus){
43952             this.on("focus", this.preFocus, this);
43953             
43954         }
43955         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43956             this.inputEl().on("keypress", this.filterKeys, this);
43957         } else {
43958             this.inputEl().relayEvent('keypress', this);
43959         }
43960         
43961         var allowed = "0123456789";
43962         
43963         if(this.allowDecimals){
43964             allowed += this.decimalSeparator;
43965         }
43966         
43967         if(this.allowNegative){
43968             allowed += "-";
43969         }
43970         
43971         if(this.thousandsDelimiter) {
43972             allowed += ",";
43973         }
43974         
43975         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43976         
43977         var keyPress = function(e){
43978             
43979             var k = e.getKey();
43980             
43981             var c = e.getCharCode();
43982             
43983             if(
43984                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43985                     allowed.indexOf(String.fromCharCode(c)) === -1
43986             ){
43987                 e.stopEvent();
43988                 return;
43989             }
43990             
43991             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43992                 return;
43993             }
43994             
43995             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43996                 e.stopEvent();
43997             }
43998         };
43999         
44000         this.inputEl().on("keypress", keyPress, this);
44001         
44002     },
44003     
44004     onTriggerClick : function(e)
44005     {   
44006         if(this.disabled){
44007             return;
44008         }
44009         
44010         this.page = 0;
44011         this.loadNext = false;
44012         
44013         if(this.isExpanded()){
44014             this.collapse();
44015             return;
44016         }
44017         
44018         this.hasFocus = true;
44019         
44020         if(this.triggerAction == 'all') {
44021             this.doQuery(this.allQuery, true);
44022             return;
44023         }
44024         
44025         this.doQuery(this.getRawValue());
44026     },
44027     
44028     getCurrency : function()
44029     {   
44030         var v = this.currencyEl().getValue();
44031         
44032         return v;
44033     },
44034     
44035     restrictHeight : function()
44036     {
44037         this.list.alignTo(this.currencyEl(), this.listAlign);
44038         this.list.alignTo(this.currencyEl(), this.listAlign);
44039     },
44040     
44041     onViewClick : function(view, doFocus, el, e)
44042     {
44043         var index = this.view.getSelectedIndexes()[0];
44044         
44045         var r = this.store.getAt(index);
44046         
44047         if(r){
44048             this.onSelect(r, index);
44049         }
44050     },
44051     
44052     onSelect : function(record, index){
44053         
44054         if(this.fireEvent('beforeselect', this, record, index) !== false){
44055         
44056             this.setFromCurrencyData(index > -1 ? record.data : false);
44057             
44058             this.collapse();
44059             
44060             this.fireEvent('select', this, record, index);
44061         }
44062     },
44063     
44064     setFromCurrencyData : function(o)
44065     {
44066         var currency = '';
44067         
44068         this.lastCurrency = o;
44069         
44070         if (this.currencyField) {
44071             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44072         } else {
44073             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44074         }
44075         
44076         this.lastSelectionText = currency;
44077         
44078         //setting default currency
44079         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44080             this.setCurrency(this.defaultCurrency);
44081             return;
44082         }
44083         
44084         this.setCurrency(currency);
44085     },
44086     
44087     setFromData : function(o)
44088     {
44089         var c = {};
44090         
44091         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44092         
44093         this.setFromCurrencyData(c);
44094         
44095         var value = '';
44096         
44097         if (this.name) {
44098             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44099         } else {
44100             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44101         }
44102         
44103         this.setValue(value);
44104         
44105     },
44106     
44107     setCurrency : function(v)
44108     {   
44109         this.currencyValue = v;
44110         
44111         if(this.rendered){
44112             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44113             this.validate();
44114         }
44115     },
44116     
44117     setValue : function(v)
44118     {
44119         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44120         
44121         this.value = v;
44122         
44123         if(this.rendered){
44124             
44125             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44126             
44127             this.inputEl().dom.value = (v == '') ? '' :
44128                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44129             
44130             if(!this.allowZero && v === '0') {
44131                 this.hiddenEl().dom.value = '';
44132                 this.inputEl().dom.value = '';
44133             }
44134             
44135             this.validate();
44136         }
44137     },
44138     
44139     getRawValue : function()
44140     {
44141         var v = this.inputEl().getValue();
44142         
44143         return v;
44144     },
44145     
44146     getValue : function()
44147     {
44148         return this.fixPrecision(this.parseValue(this.getRawValue()));
44149     },
44150     
44151     parseValue : function(value)
44152     {
44153         if(this.thousandsDelimiter) {
44154             value += "";
44155             r = new RegExp(",", "g");
44156             value = value.replace(r, "");
44157         }
44158         
44159         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44160         return isNaN(value) ? '' : value;
44161         
44162     },
44163     
44164     fixPrecision : function(value)
44165     {
44166         if(this.thousandsDelimiter) {
44167             value += "";
44168             r = new RegExp(",", "g");
44169             value = value.replace(r, "");
44170         }
44171         
44172         var nan = isNaN(value);
44173         
44174         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44175             return nan ? '' : value;
44176         }
44177         return parseFloat(value).toFixed(this.decimalPrecision);
44178     },
44179     
44180     decimalPrecisionFcn : function(v)
44181     {
44182         return Math.floor(v);
44183     },
44184     
44185     validateValue : function(value)
44186     {
44187         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44188             return false;
44189         }
44190         
44191         var num = this.parseValue(value);
44192         
44193         if(isNaN(num)){
44194             this.markInvalid(String.format(this.nanText, value));
44195             return false;
44196         }
44197         
44198         if(num < this.minValue){
44199             this.markInvalid(String.format(this.minText, this.minValue));
44200             return false;
44201         }
44202         
44203         if(num > this.maxValue){
44204             this.markInvalid(String.format(this.maxText, this.maxValue));
44205             return false;
44206         }
44207         
44208         return true;
44209     },
44210     
44211     validate : function()
44212     {
44213         if(this.disabled || this.allowBlank){
44214             this.markValid();
44215             return true;
44216         }
44217         
44218         var currency = this.getCurrency();
44219         
44220         if(this.validateValue(this.getRawValue()) && currency.length){
44221             this.markValid();
44222             return true;
44223         }
44224         
44225         this.markInvalid();
44226         return false;
44227     },
44228     
44229     getName: function()
44230     {
44231         return this.name;
44232     },
44233     
44234     beforeBlur : function()
44235     {
44236         if(!this.castInt){
44237             return;
44238         }
44239         
44240         var v = this.parseValue(this.getRawValue());
44241         
44242         if(v || v == 0){
44243             this.setValue(v);
44244         }
44245     },
44246     
44247     onBlur : function()
44248     {
44249         this.beforeBlur();
44250         
44251         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44252             //this.el.removeClass(this.focusClass);
44253         }
44254         
44255         this.hasFocus = false;
44256         
44257         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44258             this.validate();
44259         }
44260         
44261         var v = this.getValue();
44262         
44263         if(String(v) !== String(this.startValue)){
44264             this.fireEvent('change', this, v, this.startValue);
44265         }
44266         
44267         this.fireEvent("blur", this);
44268     },
44269     
44270     inputEl : function()
44271     {
44272         return this.el.select('.roo-money-amount-input', true).first();
44273     },
44274     
44275     currencyEl : function()
44276     {
44277         return this.el.select('.roo-money-currency-input', true).first();
44278     },
44279     
44280     hiddenEl : function()
44281     {
44282         return this.el.select('input.hidden-number-input',true).first();
44283     }
44284     
44285 });/**
44286  * @class Roo.bootstrap.BezierSignature
44287  * @extends Roo.bootstrap.Component
44288  * Bootstrap BezierSignature class
44289  * This script refer to:
44290  *    Title: Signature Pad
44291  *    Author: szimek
44292  *    Availability: https://github.com/szimek/signature_pad
44293  *
44294  * @constructor
44295  * Create a new BezierSignature
44296  * @param {Object} config The config object
44297  */
44298
44299 Roo.bootstrap.BezierSignature = function(config){
44300     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44301     this.addEvents({
44302         "resize" : true
44303     });
44304 };
44305
44306 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44307 {
44308      
44309     curve_data: [],
44310     
44311     is_empty: true,
44312     
44313     mouse_btn_down: true,
44314     
44315     /**
44316      * @cfg {int} canvas height
44317      */
44318     canvas_height: '200px',
44319     
44320     /**
44321      * @cfg {float|function} Radius of a single dot.
44322      */ 
44323     dot_size: false,
44324     
44325     /**
44326      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44327      */
44328     min_width: 0.5,
44329     
44330     /**
44331      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44332      */
44333     max_width: 2.5,
44334     
44335     /**
44336      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44337      */
44338     throttle: 16,
44339     
44340     /**
44341      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44342      */
44343     min_distance: 5,
44344     
44345     /**
44346      * @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.
44347      */
44348     bg_color: 'rgba(0, 0, 0, 0)',
44349     
44350     /**
44351      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44352      */
44353     dot_color: 'black',
44354     
44355     /**
44356      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44357      */ 
44358     velocity_filter_weight: 0.7,
44359     
44360     /**
44361      * @cfg {function} Callback when stroke begin. 
44362      */
44363     onBegin: false,
44364     
44365     /**
44366      * @cfg {function} Callback when stroke end.
44367      */
44368     onEnd: false,
44369     
44370     getAutoCreate : function()
44371     {
44372         var cls = 'roo-signature column';
44373         
44374         if(this.cls){
44375             cls += ' ' + this.cls;
44376         }
44377         
44378         var col_sizes = [
44379             'lg',
44380             'md',
44381             'sm',
44382             'xs'
44383         ];
44384         
44385         for(var i = 0; i < col_sizes.length; i++) {
44386             if(this[col_sizes[i]]) {
44387                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44388             }
44389         }
44390         
44391         var cfg = {
44392             tag: 'div',
44393             cls: cls,
44394             cn: [
44395                 {
44396                     tag: 'div',
44397                     cls: 'roo-signature-body',
44398                     cn: [
44399                         {
44400                             tag: 'canvas',
44401                             cls: 'roo-signature-body-canvas',
44402                             height: this.canvas_height,
44403                             width: this.canvas_width
44404                         }
44405                     ]
44406                 },
44407                 {
44408                     tag: 'input',
44409                     type: 'file',
44410                     style: 'display: none'
44411                 }
44412             ]
44413         };
44414         
44415         return cfg;
44416     },
44417     
44418     initEvents: function() 
44419     {
44420         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44421         
44422         var canvas = this.canvasEl();
44423         
44424         // mouse && touch event swapping...
44425         canvas.dom.style.touchAction = 'none';
44426         canvas.dom.style.msTouchAction = 'none';
44427         
44428         this.mouse_btn_down = false;
44429         canvas.on('mousedown', this._handleMouseDown, this);
44430         canvas.on('mousemove', this._handleMouseMove, this);
44431         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44432         
44433         if (window.PointerEvent) {
44434             canvas.on('pointerdown', this._handleMouseDown, this);
44435             canvas.on('pointermove', this._handleMouseMove, this);
44436             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44437         }
44438         
44439         if ('ontouchstart' in window) {
44440             canvas.on('touchstart', this._handleTouchStart, this);
44441             canvas.on('touchmove', this._handleTouchMove, this);
44442             canvas.on('touchend', this._handleTouchEnd, this);
44443         }
44444         
44445         Roo.EventManager.onWindowResize(this.resize, this, true);
44446         
44447         // file input event
44448         this.fileEl().on('change', this.uploadImage, this);
44449         
44450         this.clear();
44451         
44452         this.resize();
44453     },
44454     
44455     resize: function(){
44456         
44457         var canvas = this.canvasEl().dom;
44458         var ctx = this.canvasElCtx();
44459         var img_data = false;
44460         
44461         if(canvas.width > 0) {
44462             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44463         }
44464         // setting canvas width will clean img data
44465         canvas.width = 0;
44466         
44467         var style = window.getComputedStyle ? 
44468             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44469             
44470         var padding_left = parseInt(style.paddingLeft) || 0;
44471         var padding_right = parseInt(style.paddingRight) || 0;
44472         
44473         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44474         
44475         if(img_data) {
44476             ctx.putImageData(img_data, 0, 0);
44477         }
44478     },
44479     
44480     _handleMouseDown: function(e)
44481     {
44482         if (e.browserEvent.which === 1) {
44483             this.mouse_btn_down = true;
44484             this.strokeBegin(e);
44485         }
44486     },
44487     
44488     _handleMouseMove: function (e)
44489     {
44490         if (this.mouse_btn_down) {
44491             this.strokeMoveUpdate(e);
44492         }
44493     },
44494     
44495     _handleMouseUp: function (e)
44496     {
44497         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44498             this.mouse_btn_down = false;
44499             this.strokeEnd(e);
44500         }
44501     },
44502     
44503     _handleTouchStart: function (e) {
44504         
44505         e.preventDefault();
44506         if (e.browserEvent.targetTouches.length === 1) {
44507             // var touch = e.browserEvent.changedTouches[0];
44508             // this.strokeBegin(touch);
44509             
44510              this.strokeBegin(e); // assume e catching the correct xy...
44511         }
44512     },
44513     
44514     _handleTouchMove: function (e) {
44515         e.preventDefault();
44516         // var touch = event.targetTouches[0];
44517         // _this._strokeMoveUpdate(touch);
44518         this.strokeMoveUpdate(e);
44519     },
44520     
44521     _handleTouchEnd: function (e) {
44522         var wasCanvasTouched = e.target === this.canvasEl().dom;
44523         if (wasCanvasTouched) {
44524             e.preventDefault();
44525             // var touch = event.changedTouches[0];
44526             // _this._strokeEnd(touch);
44527             this.strokeEnd(e);
44528         }
44529     },
44530     
44531     reset: function () {
44532         this._lastPoints = [];
44533         this._lastVelocity = 0;
44534         this._lastWidth = (this.min_width + this.max_width) / 2;
44535         this.canvasElCtx().fillStyle = this.dot_color;
44536     },
44537     
44538     strokeMoveUpdate: function(e)
44539     {
44540         this.strokeUpdate(e);
44541         
44542         if (this.throttle) {
44543             this.throttleStroke(this.strokeUpdate, this.throttle);
44544         }
44545         else {
44546             this.strokeUpdate(e);
44547         }
44548     },
44549     
44550     strokeBegin: function(e)
44551     {
44552         var newPointGroup = {
44553             color: this.dot_color,
44554             points: []
44555         };
44556         
44557         if (typeof this.onBegin === 'function') {
44558             this.onBegin(e);
44559         }
44560         
44561         this.curve_data.push(newPointGroup);
44562         this.reset();
44563         this.strokeUpdate(e);
44564     },
44565     
44566     strokeUpdate: function(e)
44567     {
44568         var rect = this.canvasEl().dom.getBoundingClientRect();
44569         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44570         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44571         var lastPoints = lastPointGroup.points;
44572         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44573         var isLastPointTooClose = lastPoint
44574             ? point.distanceTo(lastPoint) <= this.min_distance
44575             : false;
44576         var color = lastPointGroup.color;
44577         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44578             var curve = this.addPoint(point);
44579             if (!lastPoint) {
44580                 this.drawDot({color: color, point: point});
44581             }
44582             else if (curve) {
44583                 this.drawCurve({color: color, curve: curve});
44584             }
44585             lastPoints.push({
44586                 time: point.time,
44587                 x: point.x,
44588                 y: point.y
44589             });
44590         }
44591     },
44592     
44593     strokeEnd: function(e)
44594     {
44595         this.strokeUpdate(e);
44596         if (typeof this.onEnd === 'function') {
44597             this.onEnd(e);
44598         }
44599     },
44600     
44601     addPoint:  function (point) {
44602         var _lastPoints = this._lastPoints;
44603         _lastPoints.push(point);
44604         if (_lastPoints.length > 2) {
44605             if (_lastPoints.length === 3) {
44606                 _lastPoints.unshift(_lastPoints[0]);
44607             }
44608             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44609             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44610             _lastPoints.shift();
44611             return curve;
44612         }
44613         return null;
44614     },
44615     
44616     calculateCurveWidths: function (startPoint, endPoint) {
44617         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44618             (1 - this.velocity_filter_weight) * this._lastVelocity;
44619
44620         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44621         var widths = {
44622             end: newWidth,
44623             start: this._lastWidth
44624         };
44625         
44626         this._lastVelocity = velocity;
44627         this._lastWidth = newWidth;
44628         return widths;
44629     },
44630     
44631     drawDot: function (_a) {
44632         var color = _a.color, point = _a.point;
44633         var ctx = this.canvasElCtx();
44634         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44635         ctx.beginPath();
44636         this.drawCurveSegment(point.x, point.y, width);
44637         ctx.closePath();
44638         ctx.fillStyle = color;
44639         ctx.fill();
44640     },
44641     
44642     drawCurve: function (_a) {
44643         var color = _a.color, curve = _a.curve;
44644         var ctx = this.canvasElCtx();
44645         var widthDelta = curve.endWidth - curve.startWidth;
44646         var drawSteps = Math.floor(curve.length()) * 2;
44647         ctx.beginPath();
44648         ctx.fillStyle = color;
44649         for (var i = 0; i < drawSteps; i += 1) {
44650         var t = i / drawSteps;
44651         var tt = t * t;
44652         var ttt = tt * t;
44653         var u = 1 - t;
44654         var uu = u * u;
44655         var uuu = uu * u;
44656         var x = uuu * curve.startPoint.x;
44657         x += 3 * uu * t * curve.control1.x;
44658         x += 3 * u * tt * curve.control2.x;
44659         x += ttt * curve.endPoint.x;
44660         var y = uuu * curve.startPoint.y;
44661         y += 3 * uu * t * curve.control1.y;
44662         y += 3 * u * tt * curve.control2.y;
44663         y += ttt * curve.endPoint.y;
44664         var width = curve.startWidth + ttt * widthDelta;
44665         this.drawCurveSegment(x, y, width);
44666         }
44667         ctx.closePath();
44668         ctx.fill();
44669     },
44670     
44671     drawCurveSegment: function (x, y, width) {
44672         var ctx = this.canvasElCtx();
44673         ctx.moveTo(x, y);
44674         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44675         this.is_empty = false;
44676     },
44677     
44678     clear: function()
44679     {
44680         var ctx = this.canvasElCtx();
44681         var canvas = this.canvasEl().dom;
44682         ctx.fillStyle = this.bg_color;
44683         ctx.clearRect(0, 0, canvas.width, canvas.height);
44684         ctx.fillRect(0, 0, canvas.width, canvas.height);
44685         this.curve_data = [];
44686         this.reset();
44687         this.is_empty = true;
44688     },
44689     
44690     fileEl: function()
44691     {
44692         return  this.el.select('input',true).first();
44693     },
44694     
44695     canvasEl: function()
44696     {
44697         return this.el.select('canvas',true).first();
44698     },
44699     
44700     canvasElCtx: function()
44701     {
44702         return this.el.select('canvas',true).first().dom.getContext('2d');
44703     },
44704     
44705     getImage: function(type)
44706     {
44707         if(this.is_empty) {
44708             return false;
44709         }
44710         
44711         // encryption ?
44712         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44713     },
44714     
44715     drawFromImage: function(img_src)
44716     {
44717         var img = new Image();
44718         
44719         img.onload = function(){
44720             this.canvasElCtx().drawImage(img, 0, 0);
44721         }.bind(this);
44722         
44723         img.src = img_src;
44724         
44725         this.is_empty = false;
44726     },
44727     
44728     selectImage: function()
44729     {
44730         this.fileEl().dom.click();
44731     },
44732     
44733     uploadImage: function(e)
44734     {
44735         var reader = new FileReader();
44736         
44737         reader.onload = function(e){
44738             var img = new Image();
44739             img.onload = function(){
44740                 this.reset();
44741                 this.canvasElCtx().drawImage(img, 0, 0);
44742             }.bind(this);
44743             img.src = e.target.result;
44744         }.bind(this);
44745         
44746         reader.readAsDataURL(e.target.files[0]);
44747     },
44748     
44749     // Bezier Point Constructor
44750     Point: (function () {
44751         function Point(x, y, time) {
44752             this.x = x;
44753             this.y = y;
44754             this.time = time || Date.now();
44755         }
44756         Point.prototype.distanceTo = function (start) {
44757             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44758         };
44759         Point.prototype.equals = function (other) {
44760             return this.x === other.x && this.y === other.y && this.time === other.time;
44761         };
44762         Point.prototype.velocityFrom = function (start) {
44763             return this.time !== start.time
44764             ? this.distanceTo(start) / (this.time - start.time)
44765             : 0;
44766         };
44767         return Point;
44768     }()),
44769     
44770     
44771     // Bezier Constructor
44772     Bezier: (function () {
44773         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44774             this.startPoint = startPoint;
44775             this.control2 = control2;
44776             this.control1 = control1;
44777             this.endPoint = endPoint;
44778             this.startWidth = startWidth;
44779             this.endWidth = endWidth;
44780         }
44781         Bezier.fromPoints = function (points, widths, scope) {
44782             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44783             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44784             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44785         };
44786         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44787             var dx1 = s1.x - s2.x;
44788             var dy1 = s1.y - s2.y;
44789             var dx2 = s2.x - s3.x;
44790             var dy2 = s2.y - s3.y;
44791             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44792             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44793             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44794             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44795             var dxm = m1.x - m2.x;
44796             var dym = m1.y - m2.y;
44797             var k = l2 / (l1 + l2);
44798             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44799             var tx = s2.x - cm.x;
44800             var ty = s2.y - cm.y;
44801             return {
44802                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44803                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44804             };
44805         };
44806         Bezier.prototype.length = function () {
44807             var steps = 10;
44808             var length = 0;
44809             var px;
44810             var py;
44811             for (var i = 0; i <= steps; i += 1) {
44812                 var t = i / steps;
44813                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44814                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44815                 if (i > 0) {
44816                     var xdiff = cx - px;
44817                     var ydiff = cy - py;
44818                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44819                 }
44820                 px = cx;
44821                 py = cy;
44822             }
44823             return length;
44824         };
44825         Bezier.prototype.point = function (t, start, c1, c2, end) {
44826             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44827             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44828             + (3.0 * c2 * (1.0 - t) * t * t)
44829             + (end * t * t * t);
44830         };
44831         return Bezier;
44832     }()),
44833     
44834     throttleStroke: function(fn, wait) {
44835       if (wait === void 0) { wait = 250; }
44836       var previous = 0;
44837       var timeout = null;
44838       var result;
44839       var storedContext;
44840       var storedArgs;
44841       var later = function () {
44842           previous = Date.now();
44843           timeout = null;
44844           result = fn.apply(storedContext, storedArgs);
44845           if (!timeout) {
44846               storedContext = null;
44847               storedArgs = [];
44848           }
44849       };
44850       return function wrapper() {
44851           var args = [];
44852           for (var _i = 0; _i < arguments.length; _i++) {
44853               args[_i] = arguments[_i];
44854           }
44855           var now = Date.now();
44856           var remaining = wait - (now - previous);
44857           storedContext = this;
44858           storedArgs = args;
44859           if (remaining <= 0 || remaining > wait) {
44860               if (timeout) {
44861                   clearTimeout(timeout);
44862                   timeout = null;
44863               }
44864               previous = now;
44865               result = fn.apply(storedContext, storedArgs);
44866               if (!timeout) {
44867                   storedContext = null;
44868                   storedArgs = [];
44869               }
44870           }
44871           else if (!timeout) {
44872               timeout = window.setTimeout(later, remaining);
44873           }
44874           return result;
44875       };
44876   }
44877   
44878 });
44879
44880  
44881
44882