c769875c8e17717947102333d2878d8ffecf8856
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             this.triggerEl.removeClass('open');;
3953             this.el.removeClass('show');
3954             this.hidden = true;
3955             this.fireEvent("hide", this);
3956         }
3957         if(deep === true && this.parentMenu){
3958             this.parentMenu.hide(true);
3959         }
3960     },
3961     
3962     onTriggerClick : function(e)
3963     {
3964         Roo.log('trigger click');
3965         
3966         var target = e.getTarget();
3967         
3968         Roo.log(target.nodeName.toLowerCase());
3969         
3970         if(target.nodeName.toLowerCase() === 'i'){
3971             e.preventDefault();
3972         }
3973         
3974     },
3975     
3976     onTriggerPress  : function(e)
3977     {
3978         Roo.log('trigger press');
3979         //Roo.log(e.getTarget());
3980        // Roo.log(this.triggerEl.dom);
3981        
3982         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3983         var pel = Roo.get(e.getTarget());
3984         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3985             Roo.log('is treeview or dropdown?');
3986             return;
3987         }
3988         
3989         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3990             return;
3991         }
3992         
3993         if (this.isVisible()) {
3994             Roo.log('hide');
3995             this.hide();
3996         } else {
3997             Roo.log('show');
3998              
3999             this.show(this.triggerEl, this.align, false);
4000         }
4001         
4002         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4003             e.stopEvent();
4004         }
4005         
4006     },
4007        
4008     
4009     hideMenuItems : function()
4010     {
4011         Roo.log("hide Menu Items");
4012         if (!this.el) { 
4013             return;
4014         }
4015         
4016         this.el.select('.open',true).each(function(aa) {
4017             
4018             aa.removeClass('open');
4019          
4020         });
4021     },
4022     addxtypeChild : function (tree, cntr) {
4023         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4024           
4025         this.menuitems.add(comp);
4026         return comp;
4027
4028     },
4029     getEl : function()
4030     {
4031         Roo.log(this.el);
4032         return this.el;
4033     },
4034     
4035     clear : function()
4036     {
4037         this.getEl().dom.innerHTML = '';
4038         this.menuitems.clear();
4039     }
4040 });
4041
4042  
4043  /*
4044  * - LGPL
4045  *
4046  * menu item
4047  * 
4048  */
4049
4050
4051 /**
4052  * @class Roo.bootstrap.MenuItem
4053  * @extends Roo.bootstrap.Component
4054  * Bootstrap MenuItem class
4055  * @cfg {String} html the menu label
4056  * @cfg {String} href the link
4057  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4058  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4059  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4060  * @cfg {String} fa favicon to show on left of menu item.
4061  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4062  * 
4063  * 
4064  * @constructor
4065  * Create a new MenuItem
4066  * @param {Object} config The config object
4067  */
4068
4069
4070 Roo.bootstrap.MenuItem = function(config){
4071     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4072     this.addEvents({
4073         // raw events
4074         /**
4075          * @event click
4076          * The raw click event for the entire grid.
4077          * @param {Roo.bootstrap.MenuItem} this
4078          * @param {Roo.EventObject} e
4079          */
4080         "click" : true
4081     });
4082 };
4083
4084 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4085     
4086     href : false,
4087     html : false,
4088     preventDefault: false,
4089     isContainer : false,
4090     active : false,
4091     fa: false,
4092     
4093     getAutoCreate : function(){
4094         
4095         if(this.isContainer){
4096             return {
4097                 tag: 'li',
4098                 cls: 'dropdown-menu-item '
4099             };
4100         }
4101         var ctag = {
4102             tag: 'span',
4103             html: 'Link'
4104         };
4105         
4106         var anc = {
4107             tag : 'a',
4108             cls : 'dropdown-item',
4109             href : '#',
4110             cn : [  ]
4111         };
4112         
4113         if (this.fa !== false) {
4114             anc.cn.push({
4115                 tag : 'i',
4116                 cls : 'fa fa-' + this.fa
4117             });
4118         }
4119         
4120         anc.cn.push(ctag);
4121         
4122         
4123         var cfg= {
4124             tag: 'li',
4125             cls: 'dropdown-menu-item',
4126             cn: [ anc ]
4127         };
4128         if (this.parent().type == 'treeview') {
4129             cfg.cls = 'treeview-menu';
4130         }
4131         if (this.active) {
4132             cfg.cls += ' active';
4133         }
4134         
4135         
4136         
4137         anc.href = this.href || cfg.cn[0].href ;
4138         ctag.html = this.html || cfg.cn[0].html ;
4139         return cfg;
4140     },
4141     
4142     initEvents: function()
4143     {
4144         if (this.parent().type == 'treeview') {
4145             this.el.select('a').on('click', this.onClick, this);
4146         }
4147         
4148         if (this.menu) {
4149             this.menu.parentType = this.xtype;
4150             this.menu.triggerEl = this.el;
4151             this.menu = this.addxtype(Roo.apply({}, this.menu));
4152         }
4153         
4154     },
4155     onClick : function(e)
4156     {
4157         Roo.log('item on click ');
4158         
4159         if(this.preventDefault){
4160             e.preventDefault();
4161         }
4162         //this.parent().hideMenuItems();
4163         
4164         this.fireEvent('click', this, e);
4165     },
4166     getEl : function()
4167     {
4168         return this.el;
4169     } 
4170 });
4171
4172  
4173
4174  /*
4175  * - LGPL
4176  *
4177  * menu separator
4178  * 
4179  */
4180
4181
4182 /**
4183  * @class Roo.bootstrap.MenuSeparator
4184  * @extends Roo.bootstrap.Component
4185  * Bootstrap MenuSeparator class
4186  * 
4187  * @constructor
4188  * Create a new MenuItem
4189  * @param {Object} config The config object
4190  */
4191
4192
4193 Roo.bootstrap.MenuSeparator = function(config){
4194     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4195 };
4196
4197 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4198     
4199     getAutoCreate : function(){
4200         var cfg = {
4201             cls: 'divider',
4202             tag : 'li'
4203         };
4204         
4205         return cfg;
4206     }
4207    
4208 });
4209
4210  
4211
4212  
4213 /*
4214 * Licence: LGPL
4215 */
4216
4217 /**
4218  * @class Roo.bootstrap.Modal
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap Modal class
4221  * @cfg {String} title Title of dialog
4222  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4223  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4224  * @cfg {Boolean} specificTitle default false
4225  * @cfg {Array} buttons Array of buttons or standard button set..
4226  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4227  * @cfg {Boolean} animate default true
4228  * @cfg {Boolean} allow_close default true
4229  * @cfg {Boolean} fitwindow default false
4230  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4231  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4232  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4233  * @cfg {String} size (sm|lg|xl) default empty
4234  * @cfg {Number} max_width set the max width of modal
4235  * @cfg {Boolean} editableTitle can the title be edited
4236
4237  *
4238  *
4239  * @constructor
4240  * Create a new Modal Dialog
4241  * @param {Object} config The config object
4242  */
4243
4244 Roo.bootstrap.Modal = function(config){
4245     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4246     this.addEvents({
4247         // raw events
4248         /**
4249          * @event btnclick
4250          * The raw btnclick event for the button
4251          * @param {Roo.EventObject} e
4252          */
4253         "btnclick" : true,
4254         /**
4255          * @event resize
4256          * Fire when dialog resize
4257          * @param {Roo.bootstrap.Modal} this
4258          * @param {Roo.EventObject} e
4259          */
4260         "resize" : true,
4261         /**
4262          * @event titlechanged
4263          * Fire when the editable title has been changed
4264          * @param {Roo.bootstrap.Modal} this
4265          * @param {Roo.EventObject} value
4266          */
4267         "titlechanged" : true 
4268         
4269     });
4270     this.buttons = this.buttons || [];
4271
4272     if (this.tmpl) {
4273         this.tmpl = Roo.factory(this.tmpl);
4274     }
4275
4276 };
4277
4278 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4279
4280     title : 'test dialog',
4281
4282     buttons : false,
4283
4284     // set on load...
4285
4286     html: false,
4287
4288     tmp: false,
4289
4290     specificTitle: false,
4291
4292     buttonPosition: 'right',
4293
4294     allow_close : true,
4295
4296     animate : true,
4297
4298     fitwindow: false,
4299     
4300      // private
4301     dialogEl: false,
4302     bodyEl:  false,
4303     footerEl:  false,
4304     titleEl:  false,
4305     closeEl:  false,
4306
4307     size: '',
4308     
4309     max_width: 0,
4310     
4311     max_height: 0,
4312     
4313     fit_content: false,
4314     editableTitle  : false,
4315
4316     onRender : function(ct, position)
4317     {
4318         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4319
4320         if(!this.el){
4321             var cfg = Roo.apply({},  this.getAutoCreate());
4322             cfg.id = Roo.id();
4323             //if(!cfg.name){
4324             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4325             //}
4326             //if (!cfg.name.length) {
4327             //    delete cfg.name;
4328            // }
4329             if (this.cls) {
4330                 cfg.cls += ' ' + this.cls;
4331             }
4332             if (this.style) {
4333                 cfg.style = this.style;
4334             }
4335             this.el = Roo.get(document.body).createChild(cfg, position);
4336         }
4337         //var type = this.el.dom.type;
4338
4339
4340         if(this.tabIndex !== undefined){
4341             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4342         }
4343
4344         this.dialogEl = this.el.select('.modal-dialog',true).first();
4345         this.bodyEl = this.el.select('.modal-body',true).first();
4346         this.closeEl = this.el.select('.modal-header .close', true).first();
4347         this.headerEl = this.el.select('.modal-header',true).first();
4348         this.titleEl = this.el.select('.modal-title',true).first();
4349         this.footerEl = this.el.select('.modal-footer',true).first();
4350
4351         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4352         
4353         //this.el.addClass("x-dlg-modal");
4354
4355         if (this.buttons.length) {
4356             Roo.each(this.buttons, function(bb) {
4357                 var b = Roo.apply({}, bb);
4358                 b.xns = b.xns || Roo.bootstrap;
4359                 b.xtype = b.xtype || 'Button';
4360                 if (typeof(b.listeners) == 'undefined') {
4361                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4362                 }
4363
4364                 var btn = Roo.factory(b);
4365
4366                 btn.render(this.getButtonContainer());
4367
4368             },this);
4369         }
4370         // render the children.
4371         var nitems = [];
4372
4373         if(typeof(this.items) != 'undefined'){
4374             var items = this.items;
4375             delete this.items;
4376
4377             for(var i =0;i < items.length;i++) {
4378                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4379             }
4380         }
4381
4382         this.items = nitems;
4383
4384         // where are these used - they used to be body/close/footer
4385
4386
4387         this.initEvents();
4388         //this.el.addClass([this.fieldClass, this.cls]);
4389
4390     },
4391
4392     getAutoCreate : function()
4393     {
4394         // we will default to modal-body-overflow - might need to remove or make optional later.
4395         var bdy = {
4396                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4397                 html : this.html || ''
4398         };
4399
4400         var title = {
4401             tag: 'h5',
4402             cls : 'modal-title',
4403             html : this.title
4404         };
4405
4406         if(this.specificTitle){ // WTF is this?
4407             title = this.title;
4408         }
4409
4410         var header = [];
4411         if (this.allow_close && Roo.bootstrap.version == 3) {
4412             header.push({
4413                 tag: 'button',
4414                 cls : 'close',
4415                 html : '&times'
4416             });
4417         }
4418
4419         header.push(title);
4420
4421         if (this.editableTitle) {
4422             header.push({
4423                 cls: 'form-control roo-editable-title d-none',
4424                 tag: 'input',
4425                 type: 'text'
4426             });
4427         }
4428         
4429         if (this.allow_close && Roo.bootstrap.version == 4) {
4430             header.push({
4431                 tag: 'button',
4432                 cls : 'close',
4433                 html : '&times'
4434             });
4435         }
4436         
4437         var size = '';
4438
4439         if(this.size.length){
4440             size = 'modal-' + this.size;
4441         }
4442         
4443         var footer = Roo.bootstrap.version == 3 ?
4444             {
4445                 cls : 'modal-footer',
4446                 cn : [
4447                     {
4448                         tag: 'div',
4449                         cls: 'btn-' + this.buttonPosition
4450                     }
4451                 ]
4452
4453             } :
4454             {  // BS4 uses mr-auto on left buttons....
4455                 cls : 'modal-footer'
4456             };
4457
4458             
4459
4460         
4461         
4462         var modal = {
4463             cls: "modal",
4464              cn : [
4465                 {
4466                     cls: "modal-dialog " + size,
4467                     cn : [
4468                         {
4469                             cls : "modal-content",
4470                             cn : [
4471                                 {
4472                                     cls : 'modal-header',
4473                                     cn : header
4474                                 },
4475                                 bdy,
4476                                 footer
4477                             ]
4478
4479                         }
4480                     ]
4481
4482                 }
4483             ]
4484         };
4485
4486         if(this.animate){
4487             modal.cls += ' fade';
4488         }
4489
4490         return modal;
4491
4492     },
4493     getChildContainer : function() {
4494
4495          return this.bodyEl;
4496
4497     },
4498     getButtonContainer : function() {
4499         
4500          return Roo.bootstrap.version == 4 ?
4501             this.el.select('.modal-footer',true).first()
4502             : this.el.select('.modal-footer div',true).first();
4503
4504     },
4505     initEvents : function()
4506     {
4507         if (this.allow_close) {
4508             this.closeEl.on('click', this.hide, this);
4509         }
4510         Roo.EventManager.onWindowResize(this.resize, this, true);
4511         if (this.editableTitle) {
4512             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4513             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4514             this.headerEditEl.on('keyup', function(e) {
4515                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4516                         this.toggleHeaderInput(false)
4517                     }
4518                 }, this);
4519             this.headerEditEl.on('blur', function(e) {
4520                 this.toggleHeaderInput(false)
4521             },this);
4522         }
4523
4524     },
4525   
4526
4527     resize : function()
4528     {
4529         this.maskEl.setSize(
4530             Roo.lib.Dom.getViewWidth(true),
4531             Roo.lib.Dom.getViewHeight(true)
4532         );
4533         
4534         if (this.fitwindow) {
4535             
4536            this.dialogEl.setStyle( { 'max-width' : '100%' });
4537             this.setSize(
4538                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4539                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4540             );
4541             return;
4542         }
4543         
4544         if(this.max_width !== 0) {
4545             
4546             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4547             
4548             if(this.height) {
4549                 this.setSize(w, this.height);
4550                 return;
4551             }
4552             
4553             if(this.max_height) {
4554                 this.setSize(w,Math.min(
4555                     this.max_height,
4556                     Roo.lib.Dom.getViewportHeight(true) - 60
4557                 ));
4558                 
4559                 return;
4560             }
4561             
4562             if(!this.fit_content) {
4563                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4564                 return;
4565             }
4566             
4567             this.setSize(w, Math.min(
4568                 60 +
4569                 this.headerEl.getHeight() + 
4570                 this.footerEl.getHeight() + 
4571                 this.getChildHeight(this.bodyEl.dom.childNodes),
4572                 Roo.lib.Dom.getViewportHeight(true) - 60)
4573             );
4574         }
4575         
4576     },
4577
4578     setSize : function(w,h)
4579     {
4580         if (!w && !h) {
4581             return;
4582         }
4583         
4584         this.resizeTo(w,h);
4585     },
4586
4587     show : function() {
4588
4589         if (!this.rendered) {
4590             this.render();
4591         }
4592         this.toggleHeaderInput(false);
4593         //this.el.setStyle('display', 'block');
4594         this.el.removeClass('hideing');
4595         this.el.dom.style.display='block';
4596         
4597         Roo.get(document.body).addClass('modal-open');
4598  
4599         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4600             
4601             (function(){
4602                 this.el.addClass('show');
4603                 this.el.addClass('in');
4604             }).defer(50, this);
4605         }else{
4606             this.el.addClass('show');
4607             this.el.addClass('in');
4608         }
4609
4610         // not sure how we can show data in here..
4611         //if (this.tmpl) {
4612         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4613         //}
4614
4615         Roo.get(document.body).addClass("x-body-masked");
4616         
4617         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4618         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4619         this.maskEl.dom.style.display = 'block';
4620         this.maskEl.addClass('show');
4621         
4622         
4623         this.resize();
4624         
4625         this.fireEvent('show', this);
4626
4627         // set zindex here - otherwise it appears to be ignored...
4628         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4629
4630         (function () {
4631             this.items.forEach( function(e) {
4632                 e.layout ? e.layout() : false;
4633
4634             });
4635         }).defer(100,this);
4636
4637     },
4638     hide : function()
4639     {
4640         if(this.fireEvent("beforehide", this) !== false){
4641             
4642             this.maskEl.removeClass('show');
4643             
4644             this.maskEl.dom.style.display = '';
4645             Roo.get(document.body).removeClass("x-body-masked");
4646             this.el.removeClass('in');
4647             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4648
4649             if(this.animate){ // why
4650                 this.el.addClass('hideing');
4651                 this.el.removeClass('show');
4652                 (function(){
4653                     if (!this.el.hasClass('hideing')) {
4654                         return; // it's been shown again...
4655                     }
4656                     
4657                     this.el.dom.style.display='';
4658
4659                     Roo.get(document.body).removeClass('modal-open');
4660                     this.el.removeClass('hideing');
4661                 }).defer(150,this);
4662                 
4663             }else{
4664                 this.el.removeClass('show');
4665                 this.el.dom.style.display='';
4666                 Roo.get(document.body).removeClass('modal-open');
4667
4668             }
4669             this.fireEvent('hide', this);
4670         }
4671     },
4672     isVisible : function()
4673     {
4674         
4675         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4676         
4677     },
4678
4679     addButton : function(str, cb)
4680     {
4681
4682
4683         var b = Roo.apply({}, { html : str } );
4684         b.xns = b.xns || Roo.bootstrap;
4685         b.xtype = b.xtype || 'Button';
4686         if (typeof(b.listeners) == 'undefined') {
4687             b.listeners = { click : cb.createDelegate(this)  };
4688         }
4689
4690         var btn = Roo.factory(b);
4691
4692         btn.render(this.getButtonContainer());
4693
4694         return btn;
4695
4696     },
4697
4698     setDefaultButton : function(btn)
4699     {
4700         //this.el.select('.modal-footer').()
4701     },
4702
4703     resizeTo: function(w,h)
4704     {
4705         this.dialogEl.setWidth(w);
4706         
4707         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4708
4709         this.bodyEl.setHeight(h - diff);
4710         
4711         this.fireEvent('resize', this);
4712     },
4713     
4714     setContentSize  : function(w, h)
4715     {
4716
4717     },
4718     onButtonClick: function(btn,e)
4719     {
4720         //Roo.log([a,b,c]);
4721         this.fireEvent('btnclick', btn.name, e);
4722     },
4723      /**
4724      * Set the title of the Dialog
4725      * @param {String} str new Title
4726      */
4727     setTitle: function(str) {
4728         this.titleEl.dom.innerHTML = str;
4729         this.title = str;
4730     },
4731     /**
4732      * Set the body of the Dialog
4733      * @param {String} str new Title
4734      */
4735     setBody: function(str) {
4736         this.bodyEl.dom.innerHTML = str;
4737     },
4738     /**
4739      * Set the body of the Dialog using the template
4740      * @param {Obj} data - apply this data to the template and replace the body contents.
4741      */
4742     applyBody: function(obj)
4743     {
4744         if (!this.tmpl) {
4745             Roo.log("Error - using apply Body without a template");
4746             //code
4747         }
4748         this.tmpl.overwrite(this.bodyEl, obj);
4749     },
4750     
4751     getChildHeight : function(child_nodes)
4752     {
4753         if(
4754             !child_nodes ||
4755             child_nodes.length == 0
4756         ) {
4757             return 0;
4758         }
4759         
4760         var child_height = 0;
4761         
4762         for(var i = 0; i < child_nodes.length; i++) {
4763             
4764             /*
4765             * for modal with tabs...
4766             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4767                 
4768                 var layout_childs = child_nodes[i].childNodes;
4769                 
4770                 for(var j = 0; j < layout_childs.length; j++) {
4771                     
4772                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4773                         
4774                         var layout_body_childs = layout_childs[j].childNodes;
4775                         
4776                         for(var k = 0; k < layout_body_childs.length; k++) {
4777                             
4778                             if(layout_body_childs[k].classList.contains('navbar')) {
4779                                 child_height += layout_body_childs[k].offsetHeight;
4780                                 continue;
4781                             }
4782                             
4783                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4784                                 
4785                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4786                                 
4787                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4788                                     
4789                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4790                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4791                                         continue;
4792                                     }
4793                                     
4794                                 }
4795                                 
4796                             }
4797                             
4798                         }
4799                     }
4800                 }
4801                 continue;
4802             }
4803             */
4804             
4805             child_height += child_nodes[i].offsetHeight;
4806             // Roo.log(child_nodes[i].offsetHeight);
4807         }
4808         
4809         return child_height;
4810     },
4811     toggleHeaderInput : function(is_edit)
4812     {
4813         if (!this.editableTitle) {
4814             return; // not editable.
4815         }
4816         if (is_edit && this.is_header_editing) {
4817             return; // already editing..
4818         }
4819         if (is_edit) {
4820     
4821             this.headerEditEl.dom.value = this.title;
4822             this.headerEditEl.removeClass('d-none');
4823             this.headerEditEl.dom.focus();
4824             this.titleEl.addClass('d-none');
4825             
4826             this.is_header_editing = true;
4827             return
4828         }
4829         // flip back to not editing.
4830         this.title = this.headerEditEl.dom.value;
4831         this.headerEditEl.addClass('d-none');
4832         this.titleEl.removeClass('d-none');
4833         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4834         this.is_header_editing = false;
4835         this.fireEvent('titlechanged', this, this.title);
4836     
4837             
4838         
4839     }
4840
4841 });
4842
4843
4844 Roo.apply(Roo.bootstrap.Modal,  {
4845     /**
4846          * Button config that displays a single OK button
4847          * @type Object
4848          */
4849         OK :  [{
4850             name : 'ok',
4851             weight : 'primary',
4852             html : 'OK'
4853         }],
4854         /**
4855          * Button config that displays Yes and No buttons
4856          * @type Object
4857          */
4858         YESNO : [
4859             {
4860                 name  : 'no',
4861                 html : 'No'
4862             },
4863             {
4864                 name  :'yes',
4865                 weight : 'primary',
4866                 html : 'Yes'
4867             }
4868         ],
4869
4870         /**
4871          * Button config that displays OK and Cancel buttons
4872          * @type Object
4873          */
4874         OKCANCEL : [
4875             {
4876                name : 'cancel',
4877                 html : 'Cancel'
4878             },
4879             {
4880                 name : 'ok',
4881                 weight : 'primary',
4882                 html : 'OK'
4883             }
4884         ],
4885         /**
4886          * Button config that displays Yes, No and Cancel buttons
4887          * @type Object
4888          */
4889         YESNOCANCEL : [
4890             {
4891                 name : 'yes',
4892                 weight : 'primary',
4893                 html : 'Yes'
4894             },
4895             {
4896                 name : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name : 'cancel',
4901                 html : 'Cancel'
4902             }
4903         ],
4904         
4905         zIndex : 10001
4906 });
4907
4908 /*
4909  * - LGPL
4910  *
4911  * messagebox - can be used as a replace
4912  * 
4913  */
4914 /**
4915  * @class Roo.MessageBox
4916  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4917  * Example usage:
4918  *<pre><code>
4919 // Basic alert:
4920 Roo.Msg.alert('Status', 'Changes saved successfully.');
4921
4922 // Prompt for user data:
4923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4924     if (btn == 'ok'){
4925         // process text value...
4926     }
4927 });
4928
4929 // Show a dialog using config options:
4930 Roo.Msg.show({
4931    title:'Save Changes?',
4932    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4933    buttons: Roo.Msg.YESNOCANCEL,
4934    fn: processResult,
4935    animEl: 'elId'
4936 });
4937 </code></pre>
4938  * @singleton
4939  */
4940 Roo.bootstrap.MessageBox = function(){
4941     var dlg, opt, mask, waitTimer;
4942     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4943     var buttons, activeTextEl, bwidth;
4944
4945     
4946     // private
4947     var handleButton = function(button){
4948         dlg.hide();
4949         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4950     };
4951
4952     // private
4953     var handleHide = function(){
4954         if(opt && opt.cls){
4955             dlg.el.removeClass(opt.cls);
4956         }
4957         //if(waitTimer){
4958         //    Roo.TaskMgr.stop(waitTimer);
4959         //    waitTimer = null;
4960         //}
4961     };
4962
4963     // private
4964     var updateButtons = function(b){
4965         var width = 0;
4966         if(!b){
4967             buttons["ok"].hide();
4968             buttons["cancel"].hide();
4969             buttons["yes"].hide();
4970             buttons["no"].hide();
4971             dlg.footerEl.hide();
4972             
4973             return width;
4974         }
4975         dlg.footerEl.show();
4976         for(var k in buttons){
4977             if(typeof buttons[k] != "function"){
4978                 if(b[k]){
4979                     buttons[k].show();
4980                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4981                     width += buttons[k].el.getWidth()+15;
4982                 }else{
4983                     buttons[k].hide();
4984                 }
4985             }
4986         }
4987         return width;
4988     };
4989
4990     // private
4991     var handleEsc = function(d, k, e){
4992         if(opt && opt.closable !== false){
4993             dlg.hide();
4994         }
4995         if(e){
4996             e.stopEvent();
4997         }
4998     };
4999
5000     return {
5001         /**
5002          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5003          * @return {Roo.BasicDialog} The BasicDialog element
5004          */
5005         getDialog : function(){
5006            if(!dlg){
5007                 dlg = new Roo.bootstrap.Modal( {
5008                     //draggable: true,
5009                     //resizable:false,
5010                     //constraintoviewport:false,
5011                     //fixedcenter:true,
5012                     //collapsible : false,
5013                     //shim:true,
5014                     //modal: true,
5015                 //    width: 'auto',
5016                   //  height:100,
5017                     //buttonAlign:"center",
5018                     closeClick : function(){
5019                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5020                             handleButton("no");
5021                         }else{
5022                             handleButton("cancel");
5023                         }
5024                     }
5025                 });
5026                 dlg.render();
5027                 dlg.on("hide", handleHide);
5028                 mask = dlg.mask;
5029                 //dlg.addKeyListener(27, handleEsc);
5030                 buttons = {};
5031                 this.buttons = buttons;
5032                 var bt = this.buttonText;
5033                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5034                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5035                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5036                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5037                 //Roo.log(buttons);
5038                 bodyEl = dlg.bodyEl.createChild({
5039
5040                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5041                         '<textarea class="roo-mb-textarea"></textarea>' +
5042                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5043                 });
5044                 msgEl = bodyEl.dom.firstChild;
5045                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5046                 textboxEl.enableDisplayMode();
5047                 textboxEl.addKeyListener([10,13], function(){
5048                     if(dlg.isVisible() && opt && opt.buttons){
5049                         if(opt.buttons.ok){
5050                             handleButton("ok");
5051                         }else if(opt.buttons.yes){
5052                             handleButton("yes");
5053                         }
5054                     }
5055                 });
5056                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5057                 textareaEl.enableDisplayMode();
5058                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5059                 progressEl.enableDisplayMode();
5060                 
5061                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5062                 var pf = progressEl.dom.firstChild;
5063                 if (pf) {
5064                     pp = Roo.get(pf.firstChild);
5065                     pp.setHeight(pf.offsetHeight);
5066                 }
5067                 
5068             }
5069             return dlg;
5070         },
5071
5072         /**
5073          * Updates the message box body text
5074          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5075          * the XHTML-compliant non-breaking space character '&amp;#160;')
5076          * @return {Roo.MessageBox} This message box
5077          */
5078         updateText : function(text)
5079         {
5080             if(!dlg.isVisible() && !opt.width){
5081                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5082                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5083             }
5084             msgEl.innerHTML = text || '&#160;';
5085       
5086             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5087             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5088             var w = Math.max(
5089                     Math.min(opt.width || cw , this.maxWidth), 
5090                     Math.max(opt.minWidth || this.minWidth, bwidth)
5091             );
5092             if(opt.prompt){
5093                 activeTextEl.setWidth(w);
5094             }
5095             if(dlg.isVisible()){
5096                 dlg.fixedcenter = false;
5097             }
5098             // to big, make it scroll. = But as usual stupid IE does not support
5099             // !important..
5100             
5101             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5102                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5103                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5104             } else {
5105                 bodyEl.dom.style.height = '';
5106                 bodyEl.dom.style.overflowY = '';
5107             }
5108             if (cw > w) {
5109                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5110             } else {
5111                 bodyEl.dom.style.overflowX = '';
5112             }
5113             
5114             dlg.setContentSize(w, bodyEl.getHeight());
5115             if(dlg.isVisible()){
5116                 dlg.fixedcenter = true;
5117             }
5118             return this;
5119         },
5120
5121         /**
5122          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5123          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5124          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5125          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5126          * @return {Roo.MessageBox} This message box
5127          */
5128         updateProgress : function(value, text){
5129             if(text){
5130                 this.updateText(text);
5131             }
5132             
5133             if (pp) { // weird bug on my firefox - for some reason this is not defined
5134                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5135                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5136             }
5137             return this;
5138         },        
5139
5140         /**
5141          * Returns true if the message box is currently displayed
5142          * @return {Boolean} True if the message box is visible, else false
5143          */
5144         isVisible : function(){
5145             return dlg && dlg.isVisible();  
5146         },
5147
5148         /**
5149          * Hides the message box if it is displayed
5150          */
5151         hide : function(){
5152             if(this.isVisible()){
5153                 dlg.hide();
5154             }  
5155         },
5156
5157         /**
5158          * Displays a new message box, or reinitializes an existing message box, based on the config options
5159          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5160          * The following config object properties are supported:
5161          * <pre>
5162 Property    Type             Description
5163 ----------  ---------------  ------------------------------------------------------------------------------------
5164 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5165                                    closes (defaults to undefined)
5166 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5167                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5168 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5169                                    progress and wait dialogs will ignore this property and always hide the
5170                                    close button as they can only be closed programmatically.
5171 cls               String           A custom CSS class to apply to the message box element
5172 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5173                                    displayed (defaults to 75)
5174 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5175                                    function will be btn (the name of the button that was clicked, if applicable,
5176                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5177                                    Progress and wait dialogs will ignore this option since they do not respond to
5178                                    user actions and can only be closed programmatically, so any required function
5179                                    should be called by the same code after it closes the dialog.
5180 icon              String           A CSS class that provides a background image to be used as an icon for
5181                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5182 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5183 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5184 modal             Boolean          False to allow user interaction with the page while the message box is
5185                                    displayed (defaults to true)
5186 msg               String           A string that will replace the existing message box body text (defaults
5187                                    to the XHTML-compliant non-breaking space character '&#160;')
5188 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5189 progress          Boolean          True to display a progress bar (defaults to false)
5190 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5191 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5192 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5193 title             String           The title text
5194 value             String           The string value to set into the active textbox element if displayed
5195 wait              Boolean          True to display a progress bar (defaults to false)
5196 width             Number           The width of the dialog in pixels
5197 </pre>
5198          *
5199          * Example usage:
5200          * <pre><code>
5201 Roo.Msg.show({
5202    title: 'Address',
5203    msg: 'Please enter your address:',
5204    width: 300,
5205    buttons: Roo.MessageBox.OKCANCEL,
5206    multiline: true,
5207    fn: saveAddress,
5208    animEl: 'addAddressBtn'
5209 });
5210 </code></pre>
5211          * @param {Object} config Configuration options
5212          * @return {Roo.MessageBox} This message box
5213          */
5214         show : function(options)
5215         {
5216             
5217             // this causes nightmares if you show one dialog after another
5218             // especially on callbacks..
5219              
5220             if(this.isVisible()){
5221                 
5222                 this.hide();
5223                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5224                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5225                 Roo.log("New Dialog Message:" +  options.msg )
5226                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5227                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5228                 
5229             }
5230             var d = this.getDialog();
5231             opt = options;
5232             d.setTitle(opt.title || "&#160;");
5233             d.closeEl.setDisplayed(opt.closable !== false);
5234             activeTextEl = textboxEl;
5235             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5236             if(opt.prompt){
5237                 if(opt.multiline){
5238                     textboxEl.hide();
5239                     textareaEl.show();
5240                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5241                         opt.multiline : this.defaultTextHeight);
5242                     activeTextEl = textareaEl;
5243                 }else{
5244                     textboxEl.show();
5245                     textareaEl.hide();
5246                 }
5247             }else{
5248                 textboxEl.hide();
5249                 textareaEl.hide();
5250             }
5251             progressEl.setDisplayed(opt.progress === true);
5252             if (opt.progress) {
5253                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5254             }
5255             this.updateProgress(0);
5256             activeTextEl.dom.value = opt.value || "";
5257             if(opt.prompt){
5258                 dlg.setDefaultButton(activeTextEl);
5259             }else{
5260                 var bs = opt.buttons;
5261                 var db = null;
5262                 if(bs && bs.ok){
5263                     db = buttons["ok"];
5264                 }else if(bs && bs.yes){
5265                     db = buttons["yes"];
5266                 }
5267                 dlg.setDefaultButton(db);
5268             }
5269             bwidth = updateButtons(opt.buttons);
5270             this.updateText(opt.msg);
5271             if(opt.cls){
5272                 d.el.addClass(opt.cls);
5273             }
5274             d.proxyDrag = opt.proxyDrag === true;
5275             d.modal = opt.modal !== false;
5276             d.mask = opt.modal !== false ? mask : false;
5277             if(!d.isVisible()){
5278                 // force it to the end of the z-index stack so it gets a cursor in FF
5279                 document.body.appendChild(dlg.el.dom);
5280                 d.animateTarget = null;
5281                 d.show(options.animEl);
5282             }
5283             return this;
5284         },
5285
5286         /**
5287          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5288          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5289          * and closing the message box when the process is complete.
5290          * @param {String} title The title bar text
5291          * @param {String} msg The message box body text
5292          * @return {Roo.MessageBox} This message box
5293          */
5294         progress : function(title, msg){
5295             this.show({
5296                 title : title,
5297                 msg : msg,
5298                 buttons: false,
5299                 progress:true,
5300                 closable:false,
5301                 minWidth: this.minProgressWidth,
5302                 modal : true
5303             });
5304             return this;
5305         },
5306
5307         /**
5308          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5309          * If a callback function is passed it will be called after the user clicks the button, and the
5310          * id of the button that was clicked will be passed as the only parameter to the callback
5311          * (could also be the top-right close button).
5312          * @param {String} title The title bar text
5313          * @param {String} msg The message box body text
5314          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5315          * @param {Object} scope (optional) The scope of the callback function
5316          * @return {Roo.MessageBox} This message box
5317          */
5318         alert : function(title, msg, fn, scope)
5319         {
5320             this.show({
5321                 title : title,
5322                 msg : msg,
5323                 buttons: this.OK,
5324                 fn: fn,
5325                 closable : false,
5326                 scope : scope,
5327                 modal : true
5328             });
5329             return this;
5330         },
5331
5332         /**
5333          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5334          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5335          * You are responsible for closing the message box when the process is complete.
5336          * @param {String} msg The message box body text
5337          * @param {String} title (optional) The title bar text
5338          * @return {Roo.MessageBox} This message box
5339          */
5340         wait : function(msg, title){
5341             this.show({
5342                 title : title,
5343                 msg : msg,
5344                 buttons: false,
5345                 closable:false,
5346                 progress:true,
5347                 modal:true,
5348                 width:300,
5349                 wait:true
5350             });
5351             waitTimer = Roo.TaskMgr.start({
5352                 run: function(i){
5353                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5354                 },
5355                 interval: 1000
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5362          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5363          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5364          * @param {String} title The title bar text
5365          * @param {String} msg The message box body text
5366          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5367          * @param {Object} scope (optional) The scope of the callback function
5368          * @return {Roo.MessageBox} This message box
5369          */
5370         confirm : function(title, msg, fn, scope){
5371             this.show({
5372                 title : title,
5373                 msg : msg,
5374                 buttons: this.YESNO,
5375                 fn: fn,
5376                 scope : scope,
5377                 modal : true
5378             });
5379             return this;
5380         },
5381
5382         /**
5383          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5384          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5385          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5386          * (could also be the top-right close button) and the text that was entered will be passed as the two
5387          * parameters to the callback.
5388          * @param {String} title The title bar text
5389          * @param {String} msg The message box body text
5390          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5391          * @param {Object} scope (optional) The scope of the callback function
5392          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5393          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5394          * @return {Roo.MessageBox} This message box
5395          */
5396         prompt : function(title, msg, fn, scope, multiline){
5397             this.show({
5398                 title : title,
5399                 msg : msg,
5400                 buttons: this.OKCANCEL,
5401                 fn: fn,
5402                 minWidth:250,
5403                 scope : scope,
5404                 prompt:true,
5405                 multiline: multiline,
5406                 modal : true
5407             });
5408             return this;
5409         },
5410
5411         /**
5412          * Button config that displays a single OK button
5413          * @type Object
5414          */
5415         OK : {ok:true},
5416         /**
5417          * Button config that displays Yes and No buttons
5418          * @type Object
5419          */
5420         YESNO : {yes:true, no:true},
5421         /**
5422          * Button config that displays OK and Cancel buttons
5423          * @type Object
5424          */
5425         OKCANCEL : {ok:true, cancel:true},
5426         /**
5427          * Button config that displays Yes, No and Cancel buttons
5428          * @type Object
5429          */
5430         YESNOCANCEL : {yes:true, no:true, cancel:true},
5431
5432         /**
5433          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5434          * @type Number
5435          */
5436         defaultTextHeight : 75,
5437         /**
5438          * The maximum width in pixels of the message box (defaults to 600)
5439          * @type Number
5440          */
5441         maxWidth : 600,
5442         /**
5443          * The minimum width in pixels of the message box (defaults to 100)
5444          * @type Number
5445          */
5446         minWidth : 100,
5447         /**
5448          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5449          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5450          * @type Number
5451          */
5452         minProgressWidth : 250,
5453         /**
5454          * An object containing the default button text strings that can be overriden for localized language support.
5455          * Supported properties are: ok, cancel, yes and no.
5456          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5457          * @type Object
5458          */
5459         buttonText : {
5460             ok : "OK",
5461             cancel : "Cancel",
5462             yes : "Yes",
5463             no : "No"
5464         }
5465     };
5466 }();
5467
5468 /**
5469  * Shorthand for {@link Roo.MessageBox}
5470  */
5471 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5472 Roo.Msg = Roo.Msg || Roo.MessageBox;
5473 /*
5474  * - LGPL
5475  *
5476  * navbar
5477  * 
5478  */
5479
5480 /**
5481  * @class Roo.bootstrap.Navbar
5482  * @extends Roo.bootstrap.Component
5483  * Bootstrap Navbar class
5484
5485  * @constructor
5486  * Create a new Navbar
5487  * @param {Object} config The config object
5488  */
5489
5490
5491 Roo.bootstrap.Navbar = function(config){
5492     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5493     this.addEvents({
5494         // raw events
5495         /**
5496          * @event beforetoggle
5497          * Fire before toggle the menu
5498          * @param {Roo.EventObject} e
5499          */
5500         "beforetoggle" : true
5501     });
5502 };
5503
5504 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5505     
5506     
5507    
5508     // private
5509     navItems : false,
5510     loadMask : false,
5511     
5512     
5513     getAutoCreate : function(){
5514         
5515         
5516         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5517         
5518     },
5519     
5520     initEvents :function ()
5521     {
5522         //Roo.log(this.el.select('.navbar-toggle',true));
5523         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5524         
5525         var mark = {
5526             tag: "div",
5527             cls:"x-dlg-mask"
5528         };
5529         
5530         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5531         
5532         var size = this.el.getSize();
5533         this.maskEl.setSize(size.width, size.height);
5534         this.maskEl.enableDisplayMode("block");
5535         this.maskEl.hide();
5536         
5537         if(this.loadMask){
5538             this.maskEl.show();
5539         }
5540     },
5541     
5542     
5543     getChildContainer : function()
5544     {
5545         if (this.el && this.el.select('.collapse').getCount()) {
5546             return this.el.select('.collapse',true).first();
5547         }
5548         
5549         return this.el;
5550     },
5551     
5552     mask : function()
5553     {
5554         this.maskEl.show();
5555     },
5556     
5557     unmask : function()
5558     {
5559         this.maskEl.hide();
5560     },
5561     onToggle : function()
5562     {
5563         
5564         if(this.fireEvent('beforetoggle', this) === false){
5565             return;
5566         }
5567         var ce = this.el.select('.navbar-collapse',true).first();
5568       
5569         if (!ce.hasClass('show')) {
5570            this.expand();
5571         } else {
5572             this.collapse();
5573         }
5574         
5575         
5576     
5577     },
5578     /**
5579      * Expand the navbar pulldown 
5580      */
5581     expand : function ()
5582     {
5583        
5584         var ce = this.el.select('.navbar-collapse',true).first();
5585         if (ce.hasClass('collapsing')) {
5586             return;
5587         }
5588         ce.dom.style.height = '';
5589                // show it...
5590         ce.addClass('in'); // old...
5591         ce.removeClass('collapse');
5592         ce.addClass('show');
5593         var h = ce.getHeight();
5594         Roo.log(h);
5595         ce.removeClass('show');
5596         // at this point we should be able to see it..
5597         ce.addClass('collapsing');
5598         
5599         ce.setHeight(0); // resize it ...
5600         ce.on('transitionend', function() {
5601             //Roo.log('done transition');
5602             ce.removeClass('collapsing');
5603             ce.addClass('show');
5604             ce.removeClass('collapse');
5605
5606             ce.dom.style.height = '';
5607         }, this, { single: true} );
5608         ce.setHeight(h);
5609         ce.dom.scrollTop = 0;
5610     },
5611     /**
5612      * Collapse the navbar pulldown 
5613      */
5614     collapse : function()
5615     {
5616          var ce = this.el.select('.navbar-collapse',true).first();
5617        
5618         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5619             // it's collapsed or collapsing..
5620             return;
5621         }
5622         ce.removeClass('in'); // old...
5623         ce.setHeight(ce.getHeight());
5624         ce.removeClass('show');
5625         ce.addClass('collapsing');
5626         
5627         ce.on('transitionend', function() {
5628             ce.dom.style.height = '';
5629             ce.removeClass('collapsing');
5630             ce.addClass('collapse');
5631         }, this, { single: true} );
5632         ce.setHeight(0);
5633     }
5634     
5635     
5636     
5637 });
5638
5639
5640
5641  
5642
5643  /*
5644  * - LGPL
5645  *
5646  * navbar
5647  * 
5648  */
5649
5650 /**
5651  * @class Roo.bootstrap.NavSimplebar
5652  * @extends Roo.bootstrap.Navbar
5653  * Bootstrap Sidebar class
5654  *
5655  * @cfg {Boolean} inverse is inverted color
5656  * 
5657  * @cfg {String} type (nav | pills | tabs)
5658  * @cfg {Boolean} arrangement stacked | justified
5659  * @cfg {String} align (left | right) alignment
5660  * 
5661  * @cfg {Boolean} main (true|false) main nav bar? default false
5662  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5663  * 
5664  * @cfg {String} tag (header|footer|nav|div) default is nav 
5665
5666  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5667  * 
5668  * 
5669  * @constructor
5670  * Create a new Sidebar
5671  * @param {Object} config The config object
5672  */
5673
5674
5675 Roo.bootstrap.NavSimplebar = function(config){
5676     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5677 };
5678
5679 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5680     
5681     inverse: false,
5682     
5683     type: false,
5684     arrangement: '',
5685     align : false,
5686     
5687     weight : 'light',
5688     
5689     main : false,
5690     
5691     
5692     tag : false,
5693     
5694     
5695     getAutoCreate : function(){
5696         
5697         
5698         var cfg = {
5699             tag : this.tag || 'div',
5700             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5701         };
5702         if (['light','white'].indexOf(this.weight) > -1) {
5703             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5704         }
5705         cfg.cls += ' bg-' + this.weight;
5706         
5707         if (this.inverse) {
5708             cfg.cls += ' navbar-inverse';
5709             
5710         }
5711         
5712         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5713         
5714         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5715             return cfg;
5716         }
5717         
5718         
5719     
5720         
5721         cfg.cn = [
5722             {
5723                 cls: 'nav nav-' + this.xtype,
5724                 tag : 'ul'
5725             }
5726         ];
5727         
5728          
5729         this.type = this.type || 'nav';
5730         if (['tabs','pills'].indexOf(this.type) != -1) {
5731             cfg.cn[0].cls += ' nav-' + this.type
5732         
5733         
5734         } else {
5735             if (this.type!=='nav') {
5736                 Roo.log('nav type must be nav/tabs/pills')
5737             }
5738             cfg.cn[0].cls += ' navbar-nav'
5739         }
5740         
5741         
5742         
5743         
5744         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5745             cfg.cn[0].cls += ' nav-' + this.arrangement;
5746         }
5747         
5748         
5749         if (this.align === 'right') {
5750             cfg.cn[0].cls += ' navbar-right';
5751         }
5752         
5753         
5754         
5755         
5756         return cfg;
5757     
5758         
5759     }
5760     
5761     
5762     
5763 });
5764
5765
5766
5767  
5768
5769  
5770        /*
5771  * - LGPL
5772  *
5773  * navbar
5774  * navbar-fixed-top
5775  * navbar-expand-md  fixed-top 
5776  */
5777
5778 /**
5779  * @class Roo.bootstrap.NavHeaderbar
5780  * @extends Roo.bootstrap.NavSimplebar
5781  * Bootstrap Sidebar class
5782  *
5783  * @cfg {String} brand what is brand
5784  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5785  * @cfg {String} brand_href href of the brand
5786  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5787  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5788  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5789  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5790  * 
5791  * @constructor
5792  * Create a new Sidebar
5793  * @param {Object} config The config object
5794  */
5795
5796
5797 Roo.bootstrap.NavHeaderbar = function(config){
5798     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5799       
5800 };
5801
5802 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5803     
5804     position: '',
5805     brand: '',
5806     brand_href: false,
5807     srButton : true,
5808     autohide : false,
5809     desktopCenter : false,
5810    
5811     
5812     getAutoCreate : function(){
5813         
5814         var   cfg = {
5815             tag: this.nav || 'nav',
5816             cls: 'navbar navbar-expand-md',
5817             role: 'navigation',
5818             cn: []
5819         };
5820         
5821         var cn = cfg.cn;
5822         if (this.desktopCenter) {
5823             cn.push({cls : 'container', cn : []});
5824             cn = cn[0].cn;
5825         }
5826         
5827         if(this.srButton){
5828             var btn = {
5829                 tag: 'button',
5830                 type: 'button',
5831                 cls: 'navbar-toggle navbar-toggler',
5832                 'data-toggle': 'collapse',
5833                 cn: [
5834                     {
5835                         tag: 'span',
5836                         cls: 'sr-only',
5837                         html: 'Toggle navigation'
5838                     },
5839                     {
5840                         tag: 'span',
5841                         cls: 'icon-bar navbar-toggler-icon'
5842                     },
5843                     {
5844                         tag: 'span',
5845                         cls: 'icon-bar'
5846                     },
5847                     {
5848                         tag: 'span',
5849                         cls: 'icon-bar'
5850                     }
5851                 ]
5852             };
5853             
5854             cn.push( Roo.bootstrap.version == 4 ? btn : {
5855                 tag: 'div',
5856                 cls: 'navbar-header',
5857                 cn: [
5858                     btn
5859                 ]
5860             });
5861         }
5862         
5863         cn.push({
5864             tag: 'div',
5865             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5866             cn : []
5867         });
5868         
5869         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5870         
5871         if (['light','white'].indexOf(this.weight) > -1) {
5872             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5873         }
5874         cfg.cls += ' bg-' + this.weight;
5875         
5876         
5877         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5878             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5879             
5880             // tag can override this..
5881             
5882             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5883         }
5884         
5885         if (this.brand !== '') {
5886             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5887             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5888                 tag: 'a',
5889                 href: this.brand_href ? this.brand_href : '#',
5890                 cls: 'navbar-brand',
5891                 cn: [
5892                 this.brand
5893                 ]
5894             });
5895         }
5896         
5897         if(this.main){
5898             cfg.cls += ' main-nav';
5899         }
5900         
5901         
5902         return cfg;
5903
5904         
5905     },
5906     getHeaderChildContainer : function()
5907     {
5908         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5909             return this.el.select('.navbar-header',true).first();
5910         }
5911         
5912         return this.getChildContainer();
5913     },
5914     
5915     getChildContainer : function()
5916     {
5917          
5918         return this.el.select('.roo-navbar-collapse',true).first();
5919          
5920         
5921     },
5922     
5923     initEvents : function()
5924     {
5925         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5926         
5927         if (this.autohide) {
5928             
5929             var prevScroll = 0;
5930             var ft = this.el;
5931             
5932             Roo.get(document).on('scroll',function(e) {
5933                 var ns = Roo.get(document).getScroll().top;
5934                 var os = prevScroll;
5935                 prevScroll = ns;
5936                 
5937                 if(ns > os){
5938                     ft.removeClass('slideDown');
5939                     ft.addClass('slideUp');
5940                     return;
5941                 }
5942                 ft.removeClass('slideUp');
5943                 ft.addClass('slideDown');
5944                  
5945               
5946           },this);
5947         }
5948     }    
5949     
5950 });
5951
5952
5953
5954  
5955
5956  /*
5957  * - LGPL
5958  *
5959  * navbar
5960  * 
5961  */
5962
5963 /**
5964  * @class Roo.bootstrap.NavSidebar
5965  * @extends Roo.bootstrap.Navbar
5966  * Bootstrap Sidebar class
5967  * 
5968  * @constructor
5969  * Create a new Sidebar
5970  * @param {Object} config The config object
5971  */
5972
5973
5974 Roo.bootstrap.NavSidebar = function(config){
5975     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5976 };
5977
5978 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5979     
5980     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5981     
5982     getAutoCreate : function(){
5983         
5984         
5985         return  {
5986             tag: 'div',
5987             cls: 'sidebar sidebar-nav'
5988         };
5989     
5990         
5991     }
5992     
5993     
5994     
5995 });
5996
5997
5998
5999  
6000
6001  /*
6002  * - LGPL
6003  *
6004  * nav group
6005  * 
6006  */
6007
6008 /**
6009  * @class Roo.bootstrap.NavGroup
6010  * @extends Roo.bootstrap.Component
6011  * Bootstrap NavGroup class
6012  * @cfg {String} align (left|right)
6013  * @cfg {Boolean} inverse
6014  * @cfg {String} type (nav|pills|tab) default nav
6015  * @cfg {String} navId - reference Id for navbar.
6016  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6017  * 
6018  * @constructor
6019  * Create a new nav group
6020  * @param {Object} config The config object
6021  */
6022
6023 Roo.bootstrap.NavGroup = function(config){
6024     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6025     this.navItems = [];
6026    
6027     Roo.bootstrap.NavGroup.register(this);
6028      this.addEvents({
6029         /**
6030              * @event changed
6031              * Fires when the active item changes
6032              * @param {Roo.bootstrap.NavGroup} this
6033              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6034              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6035          */
6036         'changed': true
6037      });
6038     
6039 };
6040
6041 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6042     
6043     align: '',
6044     inverse: false,
6045     form: false,
6046     type: 'nav',
6047     navId : '',
6048     // private
6049     pilltype : true,
6050     
6051     navItems : false, 
6052     
6053     getAutoCreate : function()
6054     {
6055         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6056         
6057         cfg = {
6058             tag : 'ul',
6059             cls: 'nav' 
6060         };
6061         if (Roo.bootstrap.version == 4) {
6062             if (['tabs','pills'].indexOf(this.type) != -1) {
6063                 cfg.cls += ' nav-' + this.type; 
6064             } else {
6065                 // trying to remove so header bar can right align top?
6066                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6067                     // do not use on header bar... 
6068                     cfg.cls += ' navbar-nav';
6069                 }
6070             }
6071             
6072         } else {
6073             if (['tabs','pills'].indexOf(this.type) != -1) {
6074                 cfg.cls += ' nav-' + this.type
6075             } else {
6076                 if (this.type !== 'nav') {
6077                     Roo.log('nav type must be nav/tabs/pills')
6078                 }
6079                 cfg.cls += ' navbar-nav'
6080             }
6081         }
6082         
6083         if (this.parent() && this.parent().sidebar) {
6084             cfg = {
6085                 tag: 'ul',
6086                 cls: 'dashboard-menu sidebar-menu'
6087             };
6088             
6089             return cfg;
6090         }
6091         
6092         if (this.form === true) {
6093             cfg = {
6094                 tag: 'form',
6095                 cls: 'navbar-form form-inline'
6096             };
6097             //nav navbar-right ml-md-auto
6098             if (this.align === 'right') {
6099                 cfg.cls += ' navbar-right ml-md-auto';
6100             } else {
6101                 cfg.cls += ' navbar-left';
6102             }
6103         }
6104         
6105         if (this.align === 'right') {
6106             cfg.cls += ' navbar-right ml-md-auto';
6107         } else {
6108             cfg.cls += ' mr-auto';
6109         }
6110         
6111         if (this.inverse) {
6112             cfg.cls += ' navbar-inverse';
6113             
6114         }
6115         
6116         
6117         return cfg;
6118     },
6119     /**
6120     * sets the active Navigation item
6121     * @param {Roo.bootstrap.NavItem} the new current navitem
6122     */
6123     setActiveItem : function(item)
6124     {
6125         var prev = false;
6126         Roo.each(this.navItems, function(v){
6127             if (v == item) {
6128                 return ;
6129             }
6130             if (v.isActive()) {
6131                 v.setActive(false, true);
6132                 prev = v;
6133                 
6134             }
6135             
6136         });
6137
6138         item.setActive(true, true);
6139         this.fireEvent('changed', this, item, prev);
6140         
6141         
6142     },
6143     /**
6144     * gets the active Navigation item
6145     * @return {Roo.bootstrap.NavItem} the current navitem
6146     */
6147     getActive : function()
6148     {
6149         
6150         var prev = false;
6151         Roo.each(this.navItems, function(v){
6152             
6153             if (v.isActive()) {
6154                 prev = v;
6155                 
6156             }
6157             
6158         });
6159         return prev;
6160     },
6161     
6162     indexOfNav : function()
6163     {
6164         
6165         var prev = false;
6166         Roo.each(this.navItems, function(v,i){
6167             
6168             if (v.isActive()) {
6169                 prev = i;
6170                 
6171             }
6172             
6173         });
6174         return prev;
6175     },
6176     /**
6177     * adds a Navigation item
6178     * @param {Roo.bootstrap.NavItem} the navitem to add
6179     */
6180     addItem : function(cfg)
6181     {
6182         if (this.form && Roo.bootstrap.version == 4) {
6183             cfg.tag = 'div';
6184         }
6185         var cn = new Roo.bootstrap.NavItem(cfg);
6186         this.register(cn);
6187         cn.parentId = this.id;
6188         cn.onRender(this.el, null);
6189         return cn;
6190     },
6191     /**
6192     * register a Navigation item
6193     * @param {Roo.bootstrap.NavItem} the navitem to add
6194     */
6195     register : function(item)
6196     {
6197         this.navItems.push( item);
6198         item.navId = this.navId;
6199     
6200     },
6201     
6202     /**
6203     * clear all the Navigation item
6204     */
6205    
6206     clearAll : function()
6207     {
6208         this.navItems = [];
6209         this.el.dom.innerHTML = '';
6210     },
6211     
6212     getNavItem: function(tabId)
6213     {
6214         var ret = false;
6215         Roo.each(this.navItems, function(e) {
6216             if (e.tabId == tabId) {
6217                ret =  e;
6218                return false;
6219             }
6220             return true;
6221             
6222         });
6223         return ret;
6224     },
6225     
6226     setActiveNext : function()
6227     {
6228         var i = this.indexOfNav(this.getActive());
6229         if (i > this.navItems.length) {
6230             return;
6231         }
6232         this.setActiveItem(this.navItems[i+1]);
6233     },
6234     setActivePrev : function()
6235     {
6236         var i = this.indexOfNav(this.getActive());
6237         if (i  < 1) {
6238             return;
6239         }
6240         this.setActiveItem(this.navItems[i-1]);
6241     },
6242     clearWasActive : function(except) {
6243         Roo.each(this.navItems, function(e) {
6244             if (e.tabId != except.tabId && e.was_active) {
6245                e.was_active = false;
6246                return false;
6247             }
6248             return true;
6249             
6250         });
6251     },
6252     getWasActive : function ()
6253     {
6254         var r = false;
6255         Roo.each(this.navItems, function(e) {
6256             if (e.was_active) {
6257                r = e;
6258                return false;
6259             }
6260             return true;
6261             
6262         });
6263         return r;
6264     }
6265     
6266     
6267 });
6268
6269  
6270 Roo.apply(Roo.bootstrap.NavGroup, {
6271     
6272     groups: {},
6273      /**
6274     * register a Navigation Group
6275     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6276     */
6277     register : function(navgrp)
6278     {
6279         this.groups[navgrp.navId] = navgrp;
6280         
6281     },
6282     /**
6283     * fetch a Navigation Group based on the navigation ID
6284     * @param {string} the navgroup to add
6285     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6286     */
6287     get: function(navId) {
6288         if (typeof(this.groups[navId]) == 'undefined') {
6289             return false;
6290             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6291         }
6292         return this.groups[navId] ;
6293     }
6294     
6295     
6296     
6297 });
6298
6299  /*
6300  * - LGPL
6301  *
6302  * row
6303  * 
6304  */
6305
6306 /**
6307  * @class Roo.bootstrap.NavItem
6308  * @extends Roo.bootstrap.Component
6309  * Bootstrap Navbar.NavItem class
6310  * @cfg {String} href  link to
6311  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6312  * @cfg {Boolean} button_outline show and outlined button
6313  * @cfg {String} html content of button
6314  * @cfg {String} badge text inside badge
6315  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6316  * @cfg {String} glyphicon DEPRICATED - use fa
6317  * @cfg {String} icon DEPRICATED - use fa
6318  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6319  * @cfg {Boolean} active Is item active
6320  * @cfg {Boolean} disabled Is item disabled
6321  * @cfg {String} linkcls  Link Class
6322  * @cfg {Boolean} preventDefault (true | false) default false
6323  * @cfg {String} tabId the tab that this item activates.
6324  * @cfg {String} tagtype (a|span) render as a href or span?
6325  * @cfg {Boolean} animateRef (true|false) link to element default false  
6326   
6327  * @constructor
6328  * Create a new Navbar Item
6329  * @param {Object} config The config object
6330  */
6331 Roo.bootstrap.NavItem = function(config){
6332     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6333     this.addEvents({
6334         // raw events
6335         /**
6336          * @event click
6337          * The raw click event for the entire grid.
6338          * @param {Roo.EventObject} e
6339          */
6340         "click" : true,
6341          /**
6342             * @event changed
6343             * Fires when the active item active state changes
6344             * @param {Roo.bootstrap.NavItem} this
6345             * @param {boolean} state the new state
6346              
6347          */
6348         'changed': true,
6349         /**
6350             * @event scrollto
6351             * Fires when scroll to element
6352             * @param {Roo.bootstrap.NavItem} this
6353             * @param {Object} options
6354             * @param {Roo.EventObject} e
6355              
6356          */
6357         'scrollto': true
6358     });
6359    
6360 };
6361
6362 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6363     
6364     href: false,
6365     html: '',
6366     badge: '',
6367     icon: false,
6368     fa : false,
6369     glyphicon: false,
6370     active: false,
6371     preventDefault : false,
6372     tabId : false,
6373     tagtype : 'a',
6374     tag: 'li',
6375     disabled : false,
6376     animateRef : false,
6377     was_active : false,
6378     button_weight : '',
6379     button_outline : false,
6380     linkcls : '',
6381     navLink: false,
6382     
6383     getAutoCreate : function(){
6384          
6385         var cfg = {
6386             tag: this.tag,
6387             cls: 'nav-item'
6388         };
6389         
6390         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6391         
6392         if (this.active) {
6393             cfg.cls +=  ' active' ;
6394         }
6395         if (this.disabled) {
6396             cfg.cls += ' disabled';
6397         }
6398         
6399         // BS4 only?
6400         if (this.button_weight.length) {
6401             cfg.tag = this.href ? 'a' : 'button';
6402             cfg.html = this.html || '';
6403             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6404             if (this.href) {
6405                 cfg.href = this.href;
6406             }
6407             if (this.fa) {
6408                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6409             } else {
6410                 cfg.cls += " nav-html";
6411             }
6412             
6413             // menu .. should add dropdown-menu class - so no need for carat..
6414             
6415             if (this.badge !== '') {
6416                  
6417                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6418             }
6419             return cfg;
6420         }
6421         
6422         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6423             cfg.cn = [
6424                 {
6425                     tag: this.tagtype,
6426                     href : this.href || "#",
6427                     html: this.html || '',
6428                     cls : ''
6429                 }
6430             ];
6431             if (this.tagtype == 'a') {
6432                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6433         
6434             }
6435             if (this.icon) {
6436                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6437             } else  if (this.fa) {
6438                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6439             } else if(this.glyphicon) {
6440                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6441             } else {
6442                 cfg.cn[0].cls += " nav-html";
6443             }
6444             
6445             if (this.menu) {
6446                 cfg.cn[0].html += " <span class='caret'></span>";
6447              
6448             }
6449             
6450             if (this.badge !== '') {
6451                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6452             }
6453         }
6454         
6455         
6456         
6457         return cfg;
6458     },
6459     onRender : function(ct, position)
6460     {
6461        // Roo.log("Call onRender: " + this.xtype);
6462         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6463             this.tag = 'div';
6464         }
6465         
6466         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6467         this.navLink = this.el.select('.nav-link',true).first();
6468         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6469         return ret;
6470     },
6471       
6472     
6473     initEvents: function() 
6474     {
6475         if (typeof (this.menu) != 'undefined') {
6476             this.menu.parentType = this.xtype;
6477             this.menu.triggerEl = this.el;
6478             this.menu = this.addxtype(Roo.apply({}, this.menu));
6479         }
6480         
6481         this.el.on('click', this.onClick, this);
6482         
6483         //if(this.tagtype == 'span'){
6484         //    this.el.select('span',true).on('click', this.onClick, this);
6485         //}
6486        
6487         // at this point parent should be available..
6488         this.parent().register(this);
6489     },
6490     
6491     onClick : function(e)
6492     {
6493         if (e.getTarget('.dropdown-menu-item')) {
6494             // did you click on a menu itemm.... - then don't trigger onclick..
6495             return;
6496         }
6497         
6498         if(
6499                 this.preventDefault || 
6500                 this.href == '#' 
6501         ){
6502             Roo.log("NavItem - prevent Default?");
6503             e.preventDefault();
6504         }
6505         
6506         if (this.disabled) {
6507             return;
6508         }
6509         
6510         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6511         if (tg && tg.transition) {
6512             Roo.log("waiting for the transitionend");
6513             return;
6514         }
6515         
6516         
6517         
6518         //Roo.log("fire event clicked");
6519         if(this.fireEvent('click', this, e) === false){
6520             return;
6521         };
6522         
6523         if(this.tagtype == 'span'){
6524             return;
6525         }
6526         
6527         //Roo.log(this.href);
6528         var ael = this.el.select('a',true).first();
6529         //Roo.log(ael);
6530         
6531         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6532             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6533             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6534                 return; // ignore... - it's a 'hash' to another page.
6535             }
6536             Roo.log("NavItem - prevent Default?");
6537             e.preventDefault();
6538             this.scrollToElement(e);
6539         }
6540         
6541         
6542         var p =  this.parent();
6543    
6544         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6545             if (typeof(p.setActiveItem) !== 'undefined') {
6546                 p.setActiveItem(this);
6547             }
6548         }
6549         
6550         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6551         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6552             // remove the collapsed menu expand...
6553             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6554         }
6555     },
6556     
6557     isActive: function () {
6558         return this.active
6559     },
6560     setActive : function(state, fire, is_was_active)
6561     {
6562         if (this.active && !state && this.navId) {
6563             this.was_active = true;
6564             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6565             if (nv) {
6566                 nv.clearWasActive(this);
6567             }
6568             
6569         }
6570         this.active = state;
6571         
6572         if (!state ) {
6573             this.el.removeClass('active');
6574             this.navLink ? this.navLink.removeClass('active') : false;
6575         } else if (!this.el.hasClass('active')) {
6576             
6577             this.el.addClass('active');
6578             if (Roo.bootstrap.version == 4 && this.navLink ) {
6579                 this.navLink.addClass('active');
6580             }
6581             
6582         }
6583         if (fire) {
6584             this.fireEvent('changed', this, state);
6585         }
6586         
6587         // show a panel if it's registered and related..
6588         
6589         if (!this.navId || !this.tabId || !state || is_was_active) {
6590             return;
6591         }
6592         
6593         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6594         if (!tg) {
6595             return;
6596         }
6597         var pan = tg.getPanelByName(this.tabId);
6598         if (!pan) {
6599             return;
6600         }
6601         // if we can not flip to new panel - go back to old nav highlight..
6602         if (false == tg.showPanel(pan)) {
6603             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6604             if (nv) {
6605                 var onav = nv.getWasActive();
6606                 if (onav) {
6607                     onav.setActive(true, false, true);
6608                 }
6609             }
6610             
6611         }
6612         
6613         
6614         
6615     },
6616      // this should not be here...
6617     setDisabled : function(state)
6618     {
6619         this.disabled = state;
6620         if (!state ) {
6621             this.el.removeClass('disabled');
6622         } else if (!this.el.hasClass('disabled')) {
6623             this.el.addClass('disabled');
6624         }
6625         
6626     },
6627     
6628     /**
6629      * Fetch the element to display the tooltip on.
6630      * @return {Roo.Element} defaults to this.el
6631      */
6632     tooltipEl : function()
6633     {
6634         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6635     },
6636     
6637     scrollToElement : function(e)
6638     {
6639         var c = document.body;
6640         
6641         /*
6642          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6643          */
6644         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6645             c = document.documentElement;
6646         }
6647         
6648         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6649         
6650         if(!target){
6651             return;
6652         }
6653
6654         var o = target.calcOffsetsTo(c);
6655         
6656         var options = {
6657             target : target,
6658             value : o[1]
6659         };
6660         
6661         this.fireEvent('scrollto', this, options, e);
6662         
6663         Roo.get(c).scrollTo('top', options.value, true);
6664         
6665         return;
6666     },
6667     /**
6668      * Set the HTML (text content) of the item
6669      * @param {string} html  content for the nav item
6670      */
6671     setHtml : function(html)
6672     {
6673         this.html = html;
6674         this.htmlEl.dom.innerHTML = html;
6675         
6676     } 
6677 });
6678  
6679
6680  /*
6681  * - LGPL
6682  *
6683  * sidebar item
6684  *
6685  *  li
6686  *    <span> icon </span>
6687  *    <span> text </span>
6688  *    <span>badge </span>
6689  */
6690
6691 /**
6692  * @class Roo.bootstrap.NavSidebarItem
6693  * @extends Roo.bootstrap.NavItem
6694  * Bootstrap Navbar.NavSidebarItem class
6695  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6696  * {Boolean} open is the menu open
6697  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6698  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6699  * {String} buttonSize (sm|md|lg)the extra classes for the button
6700  * {Boolean} showArrow show arrow next to the text (default true)
6701  * @constructor
6702  * Create a new Navbar Button
6703  * @param {Object} config The config object
6704  */
6705 Roo.bootstrap.NavSidebarItem = function(config){
6706     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6707     this.addEvents({
6708         // raw events
6709         /**
6710          * @event click
6711          * The raw click event for the entire grid.
6712          * @param {Roo.EventObject} e
6713          */
6714         "click" : true,
6715          /**
6716             * @event changed
6717             * Fires when the active item active state changes
6718             * @param {Roo.bootstrap.NavSidebarItem} this
6719             * @param {boolean} state the new state
6720              
6721          */
6722         'changed': true
6723     });
6724    
6725 };
6726
6727 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6728     
6729     badgeWeight : 'default',
6730     
6731     open: false,
6732     
6733     buttonView : false,
6734     
6735     buttonWeight : 'default',
6736     
6737     buttonSize : 'md',
6738     
6739     showArrow : true,
6740     
6741     getAutoCreate : function(){
6742         
6743         
6744         var a = {
6745                 tag: 'a',
6746                 href : this.href || '#',
6747                 cls: '',
6748                 html : '',
6749                 cn : []
6750         };
6751         
6752         if(this.buttonView){
6753             a = {
6754                 tag: 'button',
6755                 href : this.href || '#',
6756                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6757                 html : this.html,
6758                 cn : []
6759             };
6760         }
6761         
6762         var cfg = {
6763             tag: 'li',
6764             cls: '',
6765             cn: [ a ]
6766         };
6767         
6768         if (this.active) {
6769             cfg.cls += ' active';
6770         }
6771         
6772         if (this.disabled) {
6773             cfg.cls += ' disabled';
6774         }
6775         if (this.open) {
6776             cfg.cls += ' open x-open';
6777         }
6778         // left icon..
6779         if (this.glyphicon || this.icon) {
6780             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6781             a.cn.push({ tag : 'i', cls : c }) ;
6782         }
6783         
6784         if(!this.buttonView){
6785             var span = {
6786                 tag: 'span',
6787                 html : this.html || ''
6788             };
6789
6790             a.cn.push(span);
6791             
6792         }
6793         
6794         if (this.badge !== '') {
6795             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6796         }
6797         
6798         if (this.menu) {
6799             
6800             if(this.showArrow){
6801                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6802             }
6803             
6804             a.cls += ' dropdown-toggle treeview' ;
6805         }
6806         
6807         return cfg;
6808     },
6809     
6810     initEvents : function()
6811     { 
6812         if (typeof (this.menu) != 'undefined') {
6813             this.menu.parentType = this.xtype;
6814             this.menu.triggerEl = this.el;
6815             this.menu = this.addxtype(Roo.apply({}, this.menu));
6816         }
6817         
6818         this.el.on('click', this.onClick, this);
6819         
6820         if(this.badge !== ''){
6821             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6822         }
6823         
6824     },
6825     
6826     onClick : function(e)
6827     {
6828         if(this.disabled){
6829             e.preventDefault();
6830             return;
6831         }
6832         
6833         if(this.preventDefault){
6834             e.preventDefault();
6835         }
6836         
6837         this.fireEvent('click', this, e);
6838     },
6839     
6840     disable : function()
6841     {
6842         this.setDisabled(true);
6843     },
6844     
6845     enable : function()
6846     {
6847         this.setDisabled(false);
6848     },
6849     
6850     setDisabled : function(state)
6851     {
6852         if(this.disabled == state){
6853             return;
6854         }
6855         
6856         this.disabled = state;
6857         
6858         if (state) {
6859             this.el.addClass('disabled');
6860             return;
6861         }
6862         
6863         this.el.removeClass('disabled');
6864         
6865         return;
6866     },
6867     
6868     setActive : function(state)
6869     {
6870         if(this.active == state){
6871             return;
6872         }
6873         
6874         this.active = state;
6875         
6876         if (state) {
6877             this.el.addClass('active');
6878             return;
6879         }
6880         
6881         this.el.removeClass('active');
6882         
6883         return;
6884     },
6885     
6886     isActive: function () 
6887     {
6888         return this.active;
6889     },
6890     
6891     setBadge : function(str)
6892     {
6893         if(!this.badgeEl){
6894             return;
6895         }
6896         
6897         this.badgeEl.dom.innerHTML = str;
6898     }
6899     
6900    
6901      
6902  
6903 });
6904  
6905
6906  /*
6907  * - LGPL
6908  *
6909  *  Breadcrumb Nav
6910  * 
6911  */
6912 Roo.namespace('Roo.bootstrap.breadcrumb');
6913
6914
6915 /**
6916  * @class Roo.bootstrap.breadcrumb.Nav
6917  * @extends Roo.bootstrap.Component
6918  * Bootstrap Breadcrumb Nav Class
6919  *  
6920  * @children Roo.bootstrap.breadcrumb.Item
6921  * 
6922  * @constructor
6923  * Create a new breadcrumb.Nav
6924  * @param {Object} config The config object
6925  */
6926
6927
6928 Roo.bootstrap.breadcrumb.Nav = function(config){
6929     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6930     
6931     
6932 };
6933
6934 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6935     
6936     getAutoCreate : function()
6937     {
6938
6939         var cfg = {
6940             tag: 'nav',
6941             cn : [
6942                 {
6943                     tag : 'ol',
6944                     cls : 'breadcrumb'
6945                 }
6946             ]
6947             
6948         };
6949           
6950         return cfg;
6951     },
6952     
6953     initEvents: function()
6954     {
6955         this.olEl = this.el.select('ol',true).first();    
6956     },
6957     getChildContainer : function()
6958     {
6959         return this.olEl;  
6960     }
6961     
6962 });
6963
6964  /*
6965  * - LGPL
6966  *
6967  *  Breadcrumb Item
6968  * 
6969  */
6970
6971
6972 /**
6973  * @class Roo.bootstrap.breadcrumb.Nav
6974  * @extends Roo.bootstrap.Component
6975  * Bootstrap Breadcrumb Nav Class
6976  *  
6977  * @children Roo.bootstrap.breadcrumb.Component
6978  * @cfg {String} html the content of the link.
6979  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6980  * @cfg {Boolean} active is it active
6981
6982  * 
6983  * @constructor
6984  * Create a new breadcrumb.Nav
6985  * @param {Object} config The config object
6986  */
6987
6988 Roo.bootstrap.breadcrumb.Item = function(config){
6989     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6990     this.addEvents({
6991         // img events
6992         /**
6993          * @event click
6994          * The img click event for the img.
6995          * @param {Roo.EventObject} e
6996          */
6997         "click" : true
6998     });
6999     
7000 };
7001
7002 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7003     
7004     href: false,
7005     html : '',
7006     
7007     getAutoCreate : function()
7008     {
7009
7010         var cfg = {
7011             tag: 'li',
7012             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7013         };
7014         if (this.href !== false) {
7015             cfg.cn = [{
7016                 tag : 'a',
7017                 href : this.href,
7018                 html : this.html
7019             }];
7020         } else {
7021             cfg.html = this.html;
7022         }
7023         
7024         return cfg;
7025     },
7026     
7027     initEvents: function()
7028     {
7029         if (this.href) {
7030             this.el.select('a', true).first().on('click',this.onClick, this)
7031         }
7032         
7033     },
7034     onClick : function(e)
7035     {
7036         e.preventDefault();
7037         this.fireEvent('click',this,  e);
7038     }
7039     
7040 });
7041
7042  /*
7043  * - LGPL
7044  *
7045  * row
7046  * 
7047  */
7048
7049 /**
7050  * @class Roo.bootstrap.Row
7051  * @extends Roo.bootstrap.Component
7052  * Bootstrap Row class (contains columns...)
7053  * 
7054  * @constructor
7055  * Create a new Row
7056  * @param {Object} config The config object
7057  */
7058
7059 Roo.bootstrap.Row = function(config){
7060     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7061 };
7062
7063 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7064     
7065     getAutoCreate : function(){
7066        return {
7067             cls: 'row clearfix'
7068        };
7069     }
7070     
7071     
7072 });
7073
7074  
7075
7076  /*
7077  * - LGPL
7078  *
7079  * pagination
7080  * 
7081  */
7082
7083 /**
7084  * @class Roo.bootstrap.Pagination
7085  * @extends Roo.bootstrap.Component
7086  * Bootstrap Pagination class
7087  * @cfg {String} size xs | sm | md | lg
7088  * @cfg {Boolean} inverse false | true
7089  * 
7090  * @constructor
7091  * Create a new Pagination
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Pagination = function(config){
7096     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7100     
7101     cls: false,
7102     size: false,
7103     inverse: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = {
7107             tag: 'ul',
7108                 cls: 'pagination'
7109         };
7110         if (this.inverse) {
7111             cfg.cls += ' inverse';
7112         }
7113         if (this.html) {
7114             cfg.html=this.html;
7115         }
7116         if (this.cls) {
7117             cfg.cls += " " + this.cls;
7118         }
7119         return cfg;
7120     }
7121    
7122 });
7123
7124  
7125
7126  /*
7127  * - LGPL
7128  *
7129  * Pagination item
7130  * 
7131  */
7132
7133
7134 /**
7135  * @class Roo.bootstrap.PaginationItem
7136  * @extends Roo.bootstrap.Component
7137  * Bootstrap PaginationItem class
7138  * @cfg {String} html text
7139  * @cfg {String} href the link
7140  * @cfg {Boolean} preventDefault (true | false) default true
7141  * @cfg {Boolean} active (true | false) default false
7142  * @cfg {Boolean} disabled default false
7143  * 
7144  * 
7145  * @constructor
7146  * Create a new PaginationItem
7147  * @param {Object} config The config object
7148  */
7149
7150
7151 Roo.bootstrap.PaginationItem = function(config){
7152     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7153     this.addEvents({
7154         // raw events
7155         /**
7156          * @event click
7157          * The raw click event for the entire grid.
7158          * @param {Roo.EventObject} e
7159          */
7160         "click" : true
7161     });
7162 };
7163
7164 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7165     
7166     href : false,
7167     html : false,
7168     preventDefault: true,
7169     active : false,
7170     cls : false,
7171     disabled: false,
7172     
7173     getAutoCreate : function(){
7174         var cfg= {
7175             tag: 'li',
7176             cn: [
7177                 {
7178                     tag : 'a',
7179                     href : this.href ? this.href : '#',
7180                     html : this.html ? this.html : ''
7181                 }
7182             ]
7183         };
7184         
7185         if(this.cls){
7186             cfg.cls = this.cls;
7187         }
7188         
7189         if(this.disabled){
7190             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7191         }
7192         
7193         if(this.active){
7194             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7195         }
7196         
7197         return cfg;
7198     },
7199     
7200     initEvents: function() {
7201         
7202         this.el.on('click', this.onClick, this);
7203         
7204     },
7205     onClick : function(e)
7206     {
7207         Roo.log('PaginationItem on click ');
7208         if(this.preventDefault){
7209             e.preventDefault();
7210         }
7211         
7212         if(this.disabled){
7213             return;
7214         }
7215         
7216         this.fireEvent('click', this, e);
7217     }
7218    
7219 });
7220
7221  
7222
7223  /*
7224  * - LGPL
7225  *
7226  * slider
7227  * 
7228  */
7229
7230
7231 /**
7232  * @class Roo.bootstrap.Slider
7233  * @extends Roo.bootstrap.Component
7234  * Bootstrap Slider class
7235  *    
7236  * @constructor
7237  * Create a new Slider
7238  * @param {Object} config The config object
7239  */
7240
7241 Roo.bootstrap.Slider = function(config){
7242     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7243 };
7244
7245 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7246     
7247     getAutoCreate : function(){
7248         
7249         var cfg = {
7250             tag: 'div',
7251             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7252             cn: [
7253                 {
7254                     tag: 'a',
7255                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7256                 }
7257             ]
7258         };
7259         
7260         return cfg;
7261     }
7262    
7263 });
7264
7265  /*
7266  * Based on:
7267  * Ext JS Library 1.1.1
7268  * Copyright(c) 2006-2007, Ext JS, LLC.
7269  *
7270  * Originally Released Under LGPL - original licence link has changed is not relivant.
7271  *
7272  * Fork - LGPL
7273  * <script type="text/javascript">
7274  */
7275  
7276
7277 /**
7278  * @class Roo.grid.ColumnModel
7279  * @extends Roo.util.Observable
7280  * This is the default implementation of a ColumnModel used by the Grid. It defines
7281  * the columns in the grid.
7282  * <br>Usage:<br>
7283  <pre><code>
7284  var colModel = new Roo.grid.ColumnModel([
7285         {header: "Ticker", width: 60, sortable: true, locked: true},
7286         {header: "Company Name", width: 150, sortable: true},
7287         {header: "Market Cap.", width: 100, sortable: true},
7288         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7289         {header: "Employees", width: 100, sortable: true, resizable: false}
7290  ]);
7291  </code></pre>
7292  * <p>
7293  
7294  * The config options listed for this class are options which may appear in each
7295  * individual column definition.
7296  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7297  * @constructor
7298  * @param {Object} config An Array of column config objects. See this class's
7299  * config objects for details.
7300 */
7301 Roo.grid.ColumnModel = function(config){
7302         /**
7303      * The config passed into the constructor
7304      */
7305     this.config = config;
7306     this.lookup = {};
7307
7308     // if no id, create one
7309     // if the column does not have a dataIndex mapping,
7310     // map it to the order it is in the config
7311     for(var i = 0, len = config.length; i < len; i++){
7312         var c = config[i];
7313         if(typeof c.dataIndex == "undefined"){
7314             c.dataIndex = i;
7315         }
7316         if(typeof c.renderer == "string"){
7317             c.renderer = Roo.util.Format[c.renderer];
7318         }
7319         if(typeof c.id == "undefined"){
7320             c.id = Roo.id();
7321         }
7322         if(c.editor && c.editor.xtype){
7323             c.editor  = Roo.factory(c.editor, Roo.grid);
7324         }
7325         if(c.editor && c.editor.isFormField){
7326             c.editor = new Roo.grid.GridEditor(c.editor);
7327         }
7328         this.lookup[c.id] = c;
7329     }
7330
7331     /**
7332      * The width of columns which have no width specified (defaults to 100)
7333      * @type Number
7334      */
7335     this.defaultWidth = 100;
7336
7337     /**
7338      * Default sortable of columns which have no sortable specified (defaults to false)
7339      * @type Boolean
7340      */
7341     this.defaultSortable = false;
7342
7343     this.addEvents({
7344         /**
7345              * @event widthchange
7346              * Fires when the width of a column changes.
7347              * @param {ColumnModel} this
7348              * @param {Number} columnIndex The column index
7349              * @param {Number} newWidth The new width
7350              */
7351             "widthchange": true,
7352         /**
7353              * @event headerchange
7354              * Fires when the text of a header changes.
7355              * @param {ColumnModel} this
7356              * @param {Number} columnIndex The column index
7357              * @param {Number} newText The new header text
7358              */
7359             "headerchange": true,
7360         /**
7361              * @event hiddenchange
7362              * Fires when a column is hidden or "unhidden".
7363              * @param {ColumnModel} this
7364              * @param {Number} columnIndex The column index
7365              * @param {Boolean} hidden true if hidden, false otherwise
7366              */
7367             "hiddenchange": true,
7368             /**
7369          * @event columnmoved
7370          * Fires when a column is moved.
7371          * @param {ColumnModel} this
7372          * @param {Number} oldIndex
7373          * @param {Number} newIndex
7374          */
7375         "columnmoved" : true,
7376         /**
7377          * @event columlockchange
7378          * Fires when a column's locked state is changed
7379          * @param {ColumnModel} this
7380          * @param {Number} colIndex
7381          * @param {Boolean} locked true if locked
7382          */
7383         "columnlockchange" : true
7384     });
7385     Roo.grid.ColumnModel.superclass.constructor.call(this);
7386 };
7387 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7388     /**
7389      * @cfg {String} header The header text to display in the Grid view.
7390      */
7391     /**
7392      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7393      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7394      * specified, the column's index is used as an index into the Record's data Array.
7395      */
7396     /**
7397      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7398      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7399      */
7400     /**
7401      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7402      * Defaults to the value of the {@link #defaultSortable} property.
7403      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7404      */
7405     /**
7406      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7407      */
7408     /**
7409      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7410      */
7411     /**
7412      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7413      */
7414     /**
7415      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7416      */
7417     /**
7418      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7419      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7420      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7421      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7422      */
7423        /**
7424      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7425      */
7426     /**
7427      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7428      */
7429     /**
7430      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7431      */
7432     /**
7433      * @cfg {String} cursor (Optional)
7434      */
7435     /**
7436      * @cfg {String} tooltip (Optional)
7437      */
7438     /**
7439      * @cfg {Number} xs (Optional)
7440      */
7441     /**
7442      * @cfg {Number} sm (Optional)
7443      */
7444     /**
7445      * @cfg {Number} md (Optional)
7446      */
7447     /**
7448      * @cfg {Number} lg (Optional)
7449      */
7450     /**
7451      * Returns the id of the column at the specified index.
7452      * @param {Number} index The column index
7453      * @return {String} the id
7454      */
7455     getColumnId : function(index){
7456         return this.config[index].id;
7457     },
7458
7459     /**
7460      * Returns the column for a specified id.
7461      * @param {String} id The column id
7462      * @return {Object} the column
7463      */
7464     getColumnById : function(id){
7465         return this.lookup[id];
7466     },
7467
7468     
7469     /**
7470      * Returns the column for a specified dataIndex.
7471      * @param {String} dataIndex The column dataIndex
7472      * @return {Object|Boolean} the column or false if not found
7473      */
7474     getColumnByDataIndex: function(dataIndex){
7475         var index = this.findColumnIndex(dataIndex);
7476         return index > -1 ? this.config[index] : false;
7477     },
7478     
7479     /**
7480      * Returns the index for a specified column id.
7481      * @param {String} id The column id
7482      * @return {Number} the index, or -1 if not found
7483      */
7484     getIndexById : function(id){
7485         for(var i = 0, len = this.config.length; i < len; i++){
7486             if(this.config[i].id == id){
7487                 return i;
7488             }
7489         }
7490         return -1;
7491     },
7492     
7493     /**
7494      * Returns the index for a specified column dataIndex.
7495      * @param {String} dataIndex The column dataIndex
7496      * @return {Number} the index, or -1 if not found
7497      */
7498     
7499     findColumnIndex : function(dataIndex){
7500         for(var i = 0, len = this.config.length; i < len; i++){
7501             if(this.config[i].dataIndex == dataIndex){
7502                 return i;
7503             }
7504         }
7505         return -1;
7506     },
7507     
7508     
7509     moveColumn : function(oldIndex, newIndex){
7510         var c = this.config[oldIndex];
7511         this.config.splice(oldIndex, 1);
7512         this.config.splice(newIndex, 0, c);
7513         this.dataMap = null;
7514         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7515     },
7516
7517     isLocked : function(colIndex){
7518         return this.config[colIndex].locked === true;
7519     },
7520
7521     setLocked : function(colIndex, value, suppressEvent){
7522         if(this.isLocked(colIndex) == value){
7523             return;
7524         }
7525         this.config[colIndex].locked = value;
7526         if(!suppressEvent){
7527             this.fireEvent("columnlockchange", this, colIndex, value);
7528         }
7529     },
7530
7531     getTotalLockedWidth : function(){
7532         var totalWidth = 0;
7533         for(var i = 0; i < this.config.length; i++){
7534             if(this.isLocked(i) && !this.isHidden(i)){
7535                 this.totalWidth += this.getColumnWidth(i);
7536             }
7537         }
7538         return totalWidth;
7539     },
7540
7541     getLockedCount : function(){
7542         for(var i = 0, len = this.config.length; i < len; i++){
7543             if(!this.isLocked(i)){
7544                 return i;
7545             }
7546         }
7547         
7548         return this.config.length;
7549     },
7550
7551     /**
7552      * Returns the number of columns.
7553      * @return {Number}
7554      */
7555     getColumnCount : function(visibleOnly){
7556         if(visibleOnly === true){
7557             var c = 0;
7558             for(var i = 0, len = this.config.length; i < len; i++){
7559                 if(!this.isHidden(i)){
7560                     c++;
7561                 }
7562             }
7563             return c;
7564         }
7565         return this.config.length;
7566     },
7567
7568     /**
7569      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7570      * @param {Function} fn
7571      * @param {Object} scope (optional)
7572      * @return {Array} result
7573      */
7574     getColumnsBy : function(fn, scope){
7575         var r = [];
7576         for(var i = 0, len = this.config.length; i < len; i++){
7577             var c = this.config[i];
7578             if(fn.call(scope||this, c, i) === true){
7579                 r[r.length] = c;
7580             }
7581         }
7582         return r;
7583     },
7584
7585     /**
7586      * Returns true if the specified column is sortable.
7587      * @param {Number} col The column index
7588      * @return {Boolean}
7589      */
7590     isSortable : function(col){
7591         if(typeof this.config[col].sortable == "undefined"){
7592             return this.defaultSortable;
7593         }
7594         return this.config[col].sortable;
7595     },
7596
7597     /**
7598      * Returns the rendering (formatting) function defined for the column.
7599      * @param {Number} col The column index.
7600      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7601      */
7602     getRenderer : function(col){
7603         if(!this.config[col].renderer){
7604             return Roo.grid.ColumnModel.defaultRenderer;
7605         }
7606         return this.config[col].renderer;
7607     },
7608
7609     /**
7610      * Sets the rendering (formatting) function for a column.
7611      * @param {Number} col The column index
7612      * @param {Function} fn The function to use to process the cell's raw data
7613      * to return HTML markup for the grid view. The render function is called with
7614      * the following parameters:<ul>
7615      * <li>Data value.</li>
7616      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7617      * <li>css A CSS style string to apply to the table cell.</li>
7618      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7619      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7620      * <li>Row index</li>
7621      * <li>Column index</li>
7622      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7623      */
7624     setRenderer : function(col, fn){
7625         this.config[col].renderer = fn;
7626     },
7627
7628     /**
7629      * Returns the width for the specified column.
7630      * @param {Number} col The column index
7631      * @return {Number}
7632      */
7633     getColumnWidth : function(col){
7634         return this.config[col].width * 1 || this.defaultWidth;
7635     },
7636
7637     /**
7638      * Sets the width for a column.
7639      * @param {Number} col The column index
7640      * @param {Number} width The new width
7641      */
7642     setColumnWidth : function(col, width, suppressEvent){
7643         this.config[col].width = width;
7644         this.totalWidth = null;
7645         if(!suppressEvent){
7646              this.fireEvent("widthchange", this, col, width);
7647         }
7648     },
7649
7650     /**
7651      * Returns the total width of all columns.
7652      * @param {Boolean} includeHidden True to include hidden column widths
7653      * @return {Number}
7654      */
7655     getTotalWidth : function(includeHidden){
7656         if(!this.totalWidth){
7657             this.totalWidth = 0;
7658             for(var i = 0, len = this.config.length; i < len; i++){
7659                 if(includeHidden || !this.isHidden(i)){
7660                     this.totalWidth += this.getColumnWidth(i);
7661                 }
7662             }
7663         }
7664         return this.totalWidth;
7665     },
7666
7667     /**
7668      * Returns the header for the specified column.
7669      * @param {Number} col The column index
7670      * @return {String}
7671      */
7672     getColumnHeader : function(col){
7673         return this.config[col].header;
7674     },
7675
7676     /**
7677      * Sets the header for a column.
7678      * @param {Number} col The column index
7679      * @param {String} header The new header
7680      */
7681     setColumnHeader : function(col, header){
7682         this.config[col].header = header;
7683         this.fireEvent("headerchange", this, col, header);
7684     },
7685
7686     /**
7687      * Returns the tooltip for the specified column.
7688      * @param {Number} col The column index
7689      * @return {String}
7690      */
7691     getColumnTooltip : function(col){
7692             return this.config[col].tooltip;
7693     },
7694     /**
7695      * Sets the tooltip for a column.
7696      * @param {Number} col The column index
7697      * @param {String} tooltip The new tooltip
7698      */
7699     setColumnTooltip : function(col, tooltip){
7700             this.config[col].tooltip = tooltip;
7701     },
7702
7703     /**
7704      * Returns the dataIndex for the specified column.
7705      * @param {Number} col The column index
7706      * @return {Number}
7707      */
7708     getDataIndex : function(col){
7709         return this.config[col].dataIndex;
7710     },
7711
7712     /**
7713      * Sets the dataIndex for a column.
7714      * @param {Number} col The column index
7715      * @param {Number} dataIndex The new dataIndex
7716      */
7717     setDataIndex : function(col, dataIndex){
7718         this.config[col].dataIndex = dataIndex;
7719     },
7720
7721     
7722     
7723     /**
7724      * Returns true if the cell is editable.
7725      * @param {Number} colIndex The column index
7726      * @param {Number} rowIndex The row index - this is nto actually used..?
7727      * @return {Boolean}
7728      */
7729     isCellEditable : function(colIndex, rowIndex){
7730         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7731     },
7732
7733     /**
7734      * Returns the editor defined for the cell/column.
7735      * return false or null to disable editing.
7736      * @param {Number} colIndex The column index
7737      * @param {Number} rowIndex The row index
7738      * @return {Object}
7739      */
7740     getCellEditor : function(colIndex, rowIndex){
7741         return this.config[colIndex].editor;
7742     },
7743
7744     /**
7745      * Sets if a column is editable.
7746      * @param {Number} col The column index
7747      * @param {Boolean} editable True if the column is editable
7748      */
7749     setEditable : function(col, editable){
7750         this.config[col].editable = editable;
7751     },
7752
7753
7754     /**
7755      * Returns true if the column is hidden.
7756      * @param {Number} colIndex The column index
7757      * @return {Boolean}
7758      */
7759     isHidden : function(colIndex){
7760         return this.config[colIndex].hidden;
7761     },
7762
7763
7764     /**
7765      * Returns true if the column width cannot be changed
7766      */
7767     isFixed : function(colIndex){
7768         return this.config[colIndex].fixed;
7769     },
7770
7771     /**
7772      * Returns true if the column can be resized
7773      * @return {Boolean}
7774      */
7775     isResizable : function(colIndex){
7776         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7777     },
7778     /**
7779      * Sets if a column is hidden.
7780      * @param {Number} colIndex The column index
7781      * @param {Boolean} hidden True if the column is hidden
7782      */
7783     setHidden : function(colIndex, hidden){
7784         this.config[colIndex].hidden = hidden;
7785         this.totalWidth = null;
7786         this.fireEvent("hiddenchange", this, colIndex, hidden);
7787     },
7788
7789     /**
7790      * Sets the editor for a column.
7791      * @param {Number} col The column index
7792      * @param {Object} editor The editor object
7793      */
7794     setEditor : function(col, editor){
7795         this.config[col].editor = editor;
7796     }
7797 });
7798
7799 Roo.grid.ColumnModel.defaultRenderer = function(value)
7800 {
7801     if(typeof value == "object") {
7802         return value;
7803     }
7804         if(typeof value == "string" && value.length < 1){
7805             return "&#160;";
7806         }
7807     
7808         return String.format("{0}", value);
7809 };
7810
7811 // Alias for backwards compatibility
7812 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7813 /*
7814  * Based on:
7815  * Ext JS Library 1.1.1
7816  * Copyright(c) 2006-2007, Ext JS, LLC.
7817  *
7818  * Originally Released Under LGPL - original licence link has changed is not relivant.
7819  *
7820  * Fork - LGPL
7821  * <script type="text/javascript">
7822  */
7823  
7824 /**
7825  * @class Roo.LoadMask
7826  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7827  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7828  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7829  * element's UpdateManager load indicator and will be destroyed after the initial load.
7830  * @constructor
7831  * Create a new LoadMask
7832  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7833  * @param {Object} config The config object
7834  */
7835 Roo.LoadMask = function(el, config){
7836     this.el = Roo.get(el);
7837     Roo.apply(this, config);
7838     if(this.store){
7839         this.store.on('beforeload', this.onBeforeLoad, this);
7840         this.store.on('load', this.onLoad, this);
7841         this.store.on('loadexception', this.onLoadException, this);
7842         this.removeMask = false;
7843     }else{
7844         var um = this.el.getUpdateManager();
7845         um.showLoadIndicator = false; // disable the default indicator
7846         um.on('beforeupdate', this.onBeforeLoad, this);
7847         um.on('update', this.onLoad, this);
7848         um.on('failure', this.onLoad, this);
7849         this.removeMask = true;
7850     }
7851 };
7852
7853 Roo.LoadMask.prototype = {
7854     /**
7855      * @cfg {Boolean} removeMask
7856      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7857      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7858      */
7859     /**
7860      * @cfg {String} msg
7861      * The text to display in a centered loading message box (defaults to 'Loading...')
7862      */
7863     msg : 'Loading...',
7864     /**
7865      * @cfg {String} msgCls
7866      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7867      */
7868     msgCls : 'x-mask-loading',
7869
7870     /**
7871      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7872      * @type Boolean
7873      */
7874     disabled: false,
7875
7876     /**
7877      * Disables the mask to prevent it from being displayed
7878      */
7879     disable : function(){
7880        this.disabled = true;
7881     },
7882
7883     /**
7884      * Enables the mask so that it can be displayed
7885      */
7886     enable : function(){
7887         this.disabled = false;
7888     },
7889     
7890     onLoadException : function()
7891     {
7892         Roo.log(arguments);
7893         
7894         if (typeof(arguments[3]) != 'undefined') {
7895             Roo.MessageBox.alert("Error loading",arguments[3]);
7896         } 
7897         /*
7898         try {
7899             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7900                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7901             }   
7902         } catch(e) {
7903             
7904         }
7905         */
7906     
7907         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7908     },
7909     // private
7910     onLoad : function()
7911     {
7912         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7913     },
7914
7915     // private
7916     onBeforeLoad : function(){
7917         if(!this.disabled){
7918             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7919         }
7920     },
7921
7922     // private
7923     destroy : function(){
7924         if(this.store){
7925             this.store.un('beforeload', this.onBeforeLoad, this);
7926             this.store.un('load', this.onLoad, this);
7927             this.store.un('loadexception', this.onLoadException, this);
7928         }else{
7929             var um = this.el.getUpdateManager();
7930             um.un('beforeupdate', this.onBeforeLoad, this);
7931             um.un('update', this.onLoad, this);
7932             um.un('failure', this.onLoad, this);
7933         }
7934     }
7935 };/*
7936  * - LGPL
7937  *
7938  * table
7939  * 
7940  */
7941
7942 /**
7943  * @class Roo.bootstrap.Table
7944  * @extends Roo.bootstrap.Component
7945  * Bootstrap Table class
7946  * @cfg {String} cls table class
7947  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7948  * @cfg {String} bgcolor Specifies the background color for a table
7949  * @cfg {Number} border Specifies whether the table cells should have borders or not
7950  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7951  * @cfg {Number} cellspacing Specifies the space between cells
7952  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7953  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7954  * @cfg {String} sortable Specifies that the table should be sortable
7955  * @cfg {String} summary Specifies a summary of the content of a table
7956  * @cfg {Number} width Specifies the width of a table
7957  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7958  * 
7959  * @cfg {boolean} striped Should the rows be alternative striped
7960  * @cfg {boolean} bordered Add borders to the table
7961  * @cfg {boolean} hover Add hover highlighting
7962  * @cfg {boolean} condensed Format condensed
7963  * @cfg {boolean} responsive Format condensed
7964  * @cfg {Boolean} loadMask (true|false) default false
7965  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7966  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7967  * @cfg {Boolean} rowSelection (true|false) default false
7968  * @cfg {Boolean} cellSelection (true|false) default false
7969  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7970  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7971  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7972  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7973  
7974  * 
7975  * @constructor
7976  * Create a new Table
7977  * @param {Object} config The config object
7978  */
7979
7980 Roo.bootstrap.Table = function(config){
7981     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7982     
7983   
7984     
7985     // BC...
7986     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7987     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7988     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7989     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7990     
7991     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7992     if (this.sm) {
7993         this.sm.grid = this;
7994         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7995         this.sm = this.selModel;
7996         this.sm.xmodule = this.xmodule || false;
7997     }
7998     
7999     if (this.cm && typeof(this.cm.config) == 'undefined') {
8000         this.colModel = new Roo.grid.ColumnModel(this.cm);
8001         this.cm = this.colModel;
8002         this.cm.xmodule = this.xmodule || false;
8003     }
8004     if (this.store) {
8005         this.store= Roo.factory(this.store, Roo.data);
8006         this.ds = this.store;
8007         this.ds.xmodule = this.xmodule || false;
8008          
8009     }
8010     if (this.footer && this.store) {
8011         this.footer.dataSource = this.ds;
8012         this.footer = Roo.factory(this.footer);
8013     }
8014     
8015     /** @private */
8016     this.addEvents({
8017         /**
8018          * @event cellclick
8019          * Fires when a cell is clicked
8020          * @param {Roo.bootstrap.Table} this
8021          * @param {Roo.Element} el
8022          * @param {Number} rowIndex
8023          * @param {Number} columnIndex
8024          * @param {Roo.EventObject} e
8025          */
8026         "cellclick" : true,
8027         /**
8028          * @event celldblclick
8029          * Fires when a cell is double clicked
8030          * @param {Roo.bootstrap.Table} this
8031          * @param {Roo.Element} el
8032          * @param {Number} rowIndex
8033          * @param {Number} columnIndex
8034          * @param {Roo.EventObject} e
8035          */
8036         "celldblclick" : true,
8037         /**
8038          * @event rowclick
8039          * Fires when a row is clicked
8040          * @param {Roo.bootstrap.Table} this
8041          * @param {Roo.Element} el
8042          * @param {Number} rowIndex
8043          * @param {Roo.EventObject} e
8044          */
8045         "rowclick" : true,
8046         /**
8047          * @event rowdblclick
8048          * Fires when a row is double clicked
8049          * @param {Roo.bootstrap.Table} this
8050          * @param {Roo.Element} el
8051          * @param {Number} rowIndex
8052          * @param {Roo.EventObject} e
8053          */
8054         "rowdblclick" : true,
8055         /**
8056          * @event mouseover
8057          * Fires when a mouseover occur
8058          * @param {Roo.bootstrap.Table} this
8059          * @param {Roo.Element} el
8060          * @param {Number} rowIndex
8061          * @param {Number} columnIndex
8062          * @param {Roo.EventObject} e
8063          */
8064         "mouseover" : true,
8065         /**
8066          * @event mouseout
8067          * Fires when a mouseout occur
8068          * @param {Roo.bootstrap.Table} this
8069          * @param {Roo.Element} el
8070          * @param {Number} rowIndex
8071          * @param {Number} columnIndex
8072          * @param {Roo.EventObject} e
8073          */
8074         "mouseout" : true,
8075         /**
8076          * @event rowclass
8077          * Fires when a row is rendered, so you can change add a style to it.
8078          * @param {Roo.bootstrap.Table} this
8079          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8080          */
8081         'rowclass' : true,
8082           /**
8083          * @event rowsrendered
8084          * Fires when all the  rows have been rendered
8085          * @param {Roo.bootstrap.Table} this
8086          */
8087         'rowsrendered' : true,
8088         /**
8089          * @event contextmenu
8090          * The raw contextmenu event for the entire grid.
8091          * @param {Roo.EventObject} e
8092          */
8093         "contextmenu" : true,
8094         /**
8095          * @event rowcontextmenu
8096          * Fires when a row is right clicked
8097          * @param {Roo.bootstrap.Table} this
8098          * @param {Number} rowIndex
8099          * @param {Roo.EventObject} e
8100          */
8101         "rowcontextmenu" : true,
8102         /**
8103          * @event cellcontextmenu
8104          * Fires when a cell is right clicked
8105          * @param {Roo.bootstrap.Table} this
8106          * @param {Number} rowIndex
8107          * @param {Number} cellIndex
8108          * @param {Roo.EventObject} e
8109          */
8110          "cellcontextmenu" : true,
8111          /**
8112          * @event headercontextmenu
8113          * Fires when a header is right clicked
8114          * @param {Roo.bootstrap.Table} this
8115          * @param {Number} columnIndex
8116          * @param {Roo.EventObject} e
8117          */
8118         "headercontextmenu" : true
8119     });
8120 };
8121
8122 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8123     
8124     cls: false,
8125     align: false,
8126     bgcolor: false,
8127     border: false,
8128     cellpadding: false,
8129     cellspacing: false,
8130     frame: false,
8131     rules: false,
8132     sortable: false,
8133     summary: false,
8134     width: false,
8135     striped : false,
8136     scrollBody : false,
8137     bordered: false,
8138     hover:  false,
8139     condensed : false,
8140     responsive : false,
8141     sm : false,
8142     cm : false,
8143     store : false,
8144     loadMask : false,
8145     footerShow : true,
8146     headerShow : true,
8147   
8148     rowSelection : false,
8149     cellSelection : false,
8150     layout : false,
8151     
8152     // Roo.Element - the tbody
8153     mainBody: false,
8154     // Roo.Element - thead element
8155     mainHead: false,
8156     
8157     container: false, // used by gridpanel...
8158     
8159     lazyLoad : false,
8160     
8161     CSS : Roo.util.CSS,
8162     
8163     auto_hide_footer : false,
8164     
8165     getAutoCreate : function()
8166     {
8167         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8168         
8169         cfg = {
8170             tag: 'table',
8171             cls : 'table',
8172             cn : []
8173         };
8174         if (this.scrollBody) {
8175             cfg.cls += ' table-body-fixed';
8176         }    
8177         if (this.striped) {
8178             cfg.cls += ' table-striped';
8179         }
8180         
8181         if (this.hover) {
8182             cfg.cls += ' table-hover';
8183         }
8184         if (this.bordered) {
8185             cfg.cls += ' table-bordered';
8186         }
8187         if (this.condensed) {
8188             cfg.cls += ' table-condensed';
8189         }
8190         if (this.responsive) {
8191             cfg.cls += ' table-responsive';
8192         }
8193         
8194         if (this.cls) {
8195             cfg.cls+=  ' ' +this.cls;
8196         }
8197         
8198         // this lot should be simplifed...
8199         var _t = this;
8200         var cp = [
8201             'align',
8202             'bgcolor',
8203             'border',
8204             'cellpadding',
8205             'cellspacing',
8206             'frame',
8207             'rules',
8208             'sortable',
8209             'summary',
8210             'width'
8211         ].forEach(function(k) {
8212             if (_t[k]) {
8213                 cfg[k] = _t[k];
8214             }
8215         });
8216         
8217         
8218         if (this.layout) {
8219             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8220         }
8221         
8222         if(this.store || this.cm){
8223             if(this.headerShow){
8224                 cfg.cn.push(this.renderHeader());
8225             }
8226             
8227             cfg.cn.push(this.renderBody());
8228             
8229             if(this.footerShow){
8230                 cfg.cn.push(this.renderFooter());
8231             }
8232             // where does this come from?
8233             //cfg.cls+=  ' TableGrid';
8234         }
8235         
8236         return { cn : [ cfg ] };
8237     },
8238     
8239     initEvents : function()
8240     {   
8241         if(!this.store || !this.cm){
8242             return;
8243         }
8244         if (this.selModel) {
8245             this.selModel.initEvents();
8246         }
8247         
8248         
8249         //Roo.log('initEvents with ds!!!!');
8250         
8251         this.mainBody = this.el.select('tbody', true).first();
8252         this.mainHead = this.el.select('thead', true).first();
8253         this.mainFoot = this.el.select('tfoot', true).first();
8254         
8255         
8256         
8257         var _this = this;
8258         
8259         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8260             e.on('click', _this.sort, _this);
8261         });
8262         
8263         this.mainBody.on("click", this.onClick, this);
8264         this.mainBody.on("dblclick", this.onDblClick, this);
8265         
8266         // why is this done????? = it breaks dialogs??
8267         //this.parent().el.setStyle('position', 'relative');
8268         
8269         
8270         if (this.footer) {
8271             this.footer.parentId = this.id;
8272             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8273             
8274             if(this.lazyLoad){
8275                 this.el.select('tfoot tr td').first().addClass('hide');
8276             }
8277         } 
8278         
8279         if(this.loadMask) {
8280             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8281         }
8282         
8283         this.store.on('load', this.onLoad, this);
8284         this.store.on('beforeload', this.onBeforeLoad, this);
8285         this.store.on('update', this.onUpdate, this);
8286         this.store.on('add', this.onAdd, this);
8287         this.store.on("clear", this.clear, this);
8288         
8289         this.el.on("contextmenu", this.onContextMenu, this);
8290         
8291         this.mainBody.on('scroll', this.onBodyScroll, this);
8292         
8293         this.cm.on("headerchange", this.onHeaderChange, this);
8294         
8295         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8296         
8297     },
8298     
8299     onContextMenu : function(e, t)
8300     {
8301         this.processEvent("contextmenu", e);
8302     },
8303     
8304     processEvent : function(name, e)
8305     {
8306         if (name != 'touchstart' ) {
8307             this.fireEvent(name, e);    
8308         }
8309         
8310         var t = e.getTarget();
8311         
8312         var cell = Roo.get(t);
8313         
8314         if(!cell){
8315             return;
8316         }
8317         
8318         if(cell.findParent('tfoot', false, true)){
8319             return;
8320         }
8321         
8322         if(cell.findParent('thead', false, true)){
8323             
8324             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8325                 cell = Roo.get(t).findParent('th', false, true);
8326                 if (!cell) {
8327                     Roo.log("failed to find th in thead?");
8328                     Roo.log(e.getTarget());
8329                     return;
8330                 }
8331             }
8332             
8333             var cellIndex = cell.dom.cellIndex;
8334             
8335             var ename = name == 'touchstart' ? 'click' : name;
8336             this.fireEvent("header" + ename, this, cellIndex, e);
8337             
8338             return;
8339         }
8340         
8341         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8342             cell = Roo.get(t).findParent('td', false, true);
8343             if (!cell) {
8344                 Roo.log("failed to find th in tbody?");
8345                 Roo.log(e.getTarget());
8346                 return;
8347             }
8348         }
8349         
8350         var row = cell.findParent('tr', false, true);
8351         var cellIndex = cell.dom.cellIndex;
8352         var rowIndex = row.dom.rowIndex - 1;
8353         
8354         if(row !== false){
8355             
8356             this.fireEvent("row" + name, this, rowIndex, e);
8357             
8358             if(cell !== false){
8359             
8360                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8361             }
8362         }
8363         
8364     },
8365     
8366     onMouseover : function(e, el)
8367     {
8368         var cell = Roo.get(el);
8369         
8370         if(!cell){
8371             return;
8372         }
8373         
8374         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8375             cell = cell.findParent('td', false, true);
8376         }
8377         
8378         var row = cell.findParent('tr', false, true);
8379         var cellIndex = cell.dom.cellIndex;
8380         var rowIndex = row.dom.rowIndex - 1; // start from 0
8381         
8382         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8383         
8384     },
8385     
8386     onMouseout : function(e, el)
8387     {
8388         var cell = Roo.get(el);
8389         
8390         if(!cell){
8391             return;
8392         }
8393         
8394         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8395             cell = cell.findParent('td', false, true);
8396         }
8397         
8398         var row = cell.findParent('tr', false, true);
8399         var cellIndex = cell.dom.cellIndex;
8400         var rowIndex = row.dom.rowIndex - 1; // start from 0
8401         
8402         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8403         
8404     },
8405     
8406     onClick : function(e, el)
8407     {
8408         var cell = Roo.get(el);
8409         
8410         if(!cell || (!this.cellSelection && !this.rowSelection)){
8411             return;
8412         }
8413         
8414         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8415             cell = cell.findParent('td', false, true);
8416         }
8417         
8418         if(!cell || typeof(cell) == 'undefined'){
8419             return;
8420         }
8421         
8422         var row = cell.findParent('tr', false, true);
8423         
8424         if(!row || typeof(row) == 'undefined'){
8425             return;
8426         }
8427         
8428         var cellIndex = cell.dom.cellIndex;
8429         var rowIndex = this.getRowIndex(row);
8430         
8431         // why??? - should these not be based on SelectionModel?
8432         if(this.cellSelection){
8433             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8434         }
8435         
8436         if(this.rowSelection){
8437             this.fireEvent('rowclick', this, row, rowIndex, e);
8438         }
8439         
8440         
8441     },
8442         
8443     onDblClick : function(e,el)
8444     {
8445         var cell = Roo.get(el);
8446         
8447         if(!cell || (!this.cellSelection && !this.rowSelection)){
8448             return;
8449         }
8450         
8451         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8452             cell = cell.findParent('td', false, true);
8453         }
8454         
8455         if(!cell || typeof(cell) == 'undefined'){
8456             return;
8457         }
8458         
8459         var row = cell.findParent('tr', false, true);
8460         
8461         if(!row || typeof(row) == 'undefined'){
8462             return;
8463         }
8464         
8465         var cellIndex = cell.dom.cellIndex;
8466         var rowIndex = this.getRowIndex(row);
8467         
8468         if(this.cellSelection){
8469             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8470         }
8471         
8472         if(this.rowSelection){
8473             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8474         }
8475     },
8476     
8477     sort : function(e,el)
8478     {
8479         var col = Roo.get(el);
8480         
8481         if(!col.hasClass('sortable')){
8482             return;
8483         }
8484         
8485         var sort = col.attr('sort');
8486         var dir = 'ASC';
8487         
8488         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8489             dir = 'DESC';
8490         }
8491         
8492         this.store.sortInfo = {field : sort, direction : dir};
8493         
8494         if (this.footer) {
8495             Roo.log("calling footer first");
8496             this.footer.onClick('first');
8497         } else {
8498         
8499             this.store.load({ params : { start : 0 } });
8500         }
8501     },
8502     
8503     renderHeader : function()
8504     {
8505         var header = {
8506             tag: 'thead',
8507             cn : []
8508         };
8509         
8510         var cm = this.cm;
8511         this.totalWidth = 0;
8512         
8513         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8514             
8515             var config = cm.config[i];
8516             
8517             var c = {
8518                 tag: 'th',
8519                 cls : 'x-hcol-' + i,
8520                 style : '',
8521                 html: cm.getColumnHeader(i)
8522             };
8523             
8524             var hh = '';
8525             
8526             if(typeof(config.sortable) != 'undefined' && config.sortable){
8527                 c.cls = 'sortable';
8528                 c.html = '<i class="glyphicon"></i>' + c.html;
8529             }
8530             
8531             // could use BS4 hidden-..-down 
8532             
8533             if(typeof(config.lgHeader) != 'undefined'){
8534                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8535             }
8536             
8537             if(typeof(config.mdHeader) != 'undefined'){
8538                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8539             }
8540             
8541             if(typeof(config.smHeader) != 'undefined'){
8542                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8543             }
8544             
8545             if(typeof(config.xsHeader) != 'undefined'){
8546                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8547             }
8548             
8549             if(hh.length){
8550                 c.html = hh;
8551             }
8552             
8553             if(typeof(config.tooltip) != 'undefined'){
8554                 c.tooltip = config.tooltip;
8555             }
8556             
8557             if(typeof(config.colspan) != 'undefined'){
8558                 c.colspan = config.colspan;
8559             }
8560             
8561             if(typeof(config.hidden) != 'undefined' && config.hidden){
8562                 c.style += ' display:none;';
8563             }
8564             
8565             if(typeof(config.dataIndex) != 'undefined'){
8566                 c.sort = config.dataIndex;
8567             }
8568             
8569            
8570             
8571             if(typeof(config.align) != 'undefined' && config.align.length){
8572                 c.style += ' text-align:' + config.align + ';';
8573             }
8574             
8575             if(typeof(config.width) != 'undefined'){
8576                 c.style += ' width:' + config.width + 'px;';
8577                 this.totalWidth += config.width;
8578             } else {
8579                 this.totalWidth += 100; // assume minimum of 100 per column?
8580             }
8581             
8582             if(typeof(config.cls) != 'undefined'){
8583                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8584             }
8585             
8586             ['xs','sm','md','lg'].map(function(size){
8587                 
8588                 if(typeof(config[size]) == 'undefined'){
8589                     return;
8590                 }
8591                  
8592                 if (!config[size]) { // 0 = hidden
8593                     // BS 4 '0' is treated as hide that column and below.
8594                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8595                     return;
8596                 }
8597                 
8598                 c.cls += ' col-' + size + '-' + config[size] + (
8599                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8600                 );
8601                 
8602                 
8603             });
8604             
8605             header.cn.push(c)
8606         }
8607         
8608         return header;
8609     },
8610     
8611     renderBody : function()
8612     {
8613         var body = {
8614             tag: 'tbody',
8615             cn : [
8616                 {
8617                     tag: 'tr',
8618                     cn : [
8619                         {
8620                             tag : 'td',
8621                             colspan :  this.cm.getColumnCount()
8622                         }
8623                     ]
8624                 }
8625             ]
8626         };
8627         
8628         return body;
8629     },
8630     
8631     renderFooter : function()
8632     {
8633         var footer = {
8634             tag: 'tfoot',
8635             cn : [
8636                 {
8637                     tag: 'tr',
8638                     cn : [
8639                         {
8640                             tag : 'td',
8641                             colspan :  this.cm.getColumnCount()
8642                         }
8643                     ]
8644                 }
8645             ]
8646         };
8647         
8648         return footer;
8649     },
8650     
8651     
8652     
8653     onLoad : function()
8654     {
8655 //        Roo.log('ds onload');
8656         this.clear();
8657         
8658         var _this = this;
8659         var cm = this.cm;
8660         var ds = this.store;
8661         
8662         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8663             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8664             if (_this.store.sortInfo) {
8665                     
8666                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8667                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8668                 }
8669                 
8670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8671                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8672                 }
8673             }
8674         });
8675         
8676         var tbody =  this.mainBody;
8677               
8678         if(ds.getCount() > 0){
8679             ds.data.each(function(d,rowIndex){
8680                 var row =  this.renderRow(cm, ds, rowIndex);
8681                 
8682                 tbody.createChild(row);
8683                 
8684                 var _this = this;
8685                 
8686                 if(row.cellObjects.length){
8687                     Roo.each(row.cellObjects, function(r){
8688                         _this.renderCellObject(r);
8689                     })
8690                 }
8691                 
8692             }, this);
8693         }
8694         
8695         var tfoot = this.el.select('tfoot', true).first();
8696         
8697         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8698             
8699             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8700             
8701             var total = this.ds.getTotalCount();
8702             
8703             if(this.footer.pageSize < total){
8704                 this.mainFoot.show();
8705             }
8706         }
8707         
8708         Roo.each(this.el.select('tbody td', true).elements, function(e){
8709             e.on('mouseover', _this.onMouseover, _this);
8710         });
8711         
8712         Roo.each(this.el.select('tbody td', true).elements, function(e){
8713             e.on('mouseout', _this.onMouseout, _this);
8714         });
8715         this.fireEvent('rowsrendered', this);
8716         
8717         this.autoSize();
8718     },
8719     
8720     
8721     onUpdate : function(ds,record)
8722     {
8723         this.refreshRow(record);
8724         this.autoSize();
8725     },
8726     
8727     onRemove : function(ds, record, index, isUpdate){
8728         if(isUpdate !== true){
8729             this.fireEvent("beforerowremoved", this, index, record);
8730         }
8731         var bt = this.mainBody.dom;
8732         
8733         var rows = this.el.select('tbody > tr', true).elements;
8734         
8735         if(typeof(rows[index]) != 'undefined'){
8736             bt.removeChild(rows[index].dom);
8737         }
8738         
8739 //        if(bt.rows[index]){
8740 //            bt.removeChild(bt.rows[index]);
8741 //        }
8742         
8743         if(isUpdate !== true){
8744             //this.stripeRows(index);
8745             //this.syncRowHeights(index, index);
8746             //this.layout();
8747             this.fireEvent("rowremoved", this, index, record);
8748         }
8749     },
8750     
8751     onAdd : function(ds, records, rowIndex)
8752     {
8753         //Roo.log('on Add called');
8754         // - note this does not handle multiple adding very well..
8755         var bt = this.mainBody.dom;
8756         for (var i =0 ; i < records.length;i++) {
8757             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8758             //Roo.log(records[i]);
8759             //Roo.log(this.store.getAt(rowIndex+i));
8760             this.insertRow(this.store, rowIndex + i, false);
8761             return;
8762         }
8763         
8764     },
8765     
8766     
8767     refreshRow : function(record){
8768         var ds = this.store, index;
8769         if(typeof record == 'number'){
8770             index = record;
8771             record = ds.getAt(index);
8772         }else{
8773             index = ds.indexOf(record);
8774             if (index < 0) {
8775                 return; // should not happen - but seems to 
8776             }
8777         }
8778         this.insertRow(ds, index, true);
8779         this.autoSize();
8780         this.onRemove(ds, record, index+1, true);
8781         this.autoSize();
8782         //this.syncRowHeights(index, index);
8783         //this.layout();
8784         this.fireEvent("rowupdated", this, index, record);
8785     },
8786     
8787     insertRow : function(dm, rowIndex, isUpdate){
8788         
8789         if(!isUpdate){
8790             this.fireEvent("beforerowsinserted", this, rowIndex);
8791         }
8792             //var s = this.getScrollState();
8793         var row = this.renderRow(this.cm, this.store, rowIndex);
8794         // insert before rowIndex..
8795         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8796         
8797         var _this = this;
8798                 
8799         if(row.cellObjects.length){
8800             Roo.each(row.cellObjects, function(r){
8801                 _this.renderCellObject(r);
8802             })
8803         }
8804             
8805         if(!isUpdate){
8806             this.fireEvent("rowsinserted", this, rowIndex);
8807             //this.syncRowHeights(firstRow, lastRow);
8808             //this.stripeRows(firstRow);
8809             //this.layout();
8810         }
8811         
8812     },
8813     
8814     
8815     getRowDom : function(rowIndex)
8816     {
8817         var rows = this.el.select('tbody > tr', true).elements;
8818         
8819         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8820         
8821     },
8822     // returns the object tree for a tr..
8823   
8824     
8825     renderRow : function(cm, ds, rowIndex) 
8826     {
8827         var d = ds.getAt(rowIndex);
8828         
8829         var row = {
8830             tag : 'tr',
8831             cls : 'x-row-' + rowIndex,
8832             cn : []
8833         };
8834             
8835         var cellObjects = [];
8836         
8837         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8838             var config = cm.config[i];
8839             
8840             var renderer = cm.getRenderer(i);
8841             var value = '';
8842             var id = false;
8843             
8844             if(typeof(renderer) !== 'undefined'){
8845                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8846             }
8847             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8848             // and are rendered into the cells after the row is rendered - using the id for the element.
8849             
8850             if(typeof(value) === 'object'){
8851                 id = Roo.id();
8852                 cellObjects.push({
8853                     container : id,
8854                     cfg : value 
8855                 })
8856             }
8857             
8858             var rowcfg = {
8859                 record: d,
8860                 rowIndex : rowIndex,
8861                 colIndex : i,
8862                 rowClass : ''
8863             };
8864
8865             this.fireEvent('rowclass', this, rowcfg);
8866             
8867             var td = {
8868                 tag: 'td',
8869                 cls : rowcfg.rowClass + ' x-col-' + i,
8870                 style: '',
8871                 html: (typeof(value) === 'object') ? '' : value
8872             };
8873             
8874             if (id) {
8875                 td.id = id;
8876             }
8877             
8878             if(typeof(config.colspan) != 'undefined'){
8879                 td.colspan = config.colspan;
8880             }
8881             
8882             if(typeof(config.hidden) != 'undefined' && config.hidden){
8883                 td.style += ' display:none;';
8884             }
8885             
8886             if(typeof(config.align) != 'undefined' && config.align.length){
8887                 td.style += ' text-align:' + config.align + ';';
8888             }
8889             if(typeof(config.valign) != 'undefined' && config.valign.length){
8890                 td.style += ' vertical-align:' + config.valign + ';';
8891             }
8892             
8893             if(typeof(config.width) != 'undefined'){
8894                 td.style += ' width:' +  config.width + 'px;';
8895             }
8896             
8897             if(typeof(config.cursor) != 'undefined'){
8898                 td.style += ' cursor:' +  config.cursor + ';';
8899             }
8900             
8901             if(typeof(config.cls) != 'undefined'){
8902                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8903             }
8904             
8905             ['xs','sm','md','lg'].map(function(size){
8906                 
8907                 if(typeof(config[size]) == 'undefined'){
8908                     return;
8909                 }
8910                 
8911                 
8912                   
8913                 if (!config[size]) { // 0 = hidden
8914                     // BS 4 '0' is treated as hide that column and below.
8915                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8916                     return;
8917                 }
8918                 
8919                 td.cls += ' col-' + size + '-' + config[size] + (
8920                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8921                 );
8922                  
8923
8924             });
8925             
8926             row.cn.push(td);
8927            
8928         }
8929         
8930         row.cellObjects = cellObjects;
8931         
8932         return row;
8933           
8934     },
8935     
8936     
8937     
8938     onBeforeLoad : function()
8939     {
8940         
8941     },
8942      /**
8943      * Remove all rows
8944      */
8945     clear : function()
8946     {
8947         this.el.select('tbody', true).first().dom.innerHTML = '';
8948     },
8949     /**
8950      * Show or hide a row.
8951      * @param {Number} rowIndex to show or hide
8952      * @param {Boolean} state hide
8953      */
8954     setRowVisibility : function(rowIndex, state)
8955     {
8956         var bt = this.mainBody.dom;
8957         
8958         var rows = this.el.select('tbody > tr', true).elements;
8959         
8960         if(typeof(rows[rowIndex]) == 'undefined'){
8961             return;
8962         }
8963         rows[rowIndex].dom.style.display = state ? '' : 'none';
8964     },
8965     
8966     
8967     getSelectionModel : function(){
8968         if(!this.selModel){
8969             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8970         }
8971         return this.selModel;
8972     },
8973     /*
8974      * Render the Roo.bootstrap object from renderder
8975      */
8976     renderCellObject : function(r)
8977     {
8978         var _this = this;
8979         
8980         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8981         
8982         var t = r.cfg.render(r.container);
8983         
8984         if(r.cfg.cn){
8985             Roo.each(r.cfg.cn, function(c){
8986                 var child = {
8987                     container: t.getChildContainer(),
8988                     cfg: c
8989                 };
8990                 _this.renderCellObject(child);
8991             })
8992         }
8993     },
8994     
8995     getRowIndex : function(row)
8996     {
8997         var rowIndex = -1;
8998         
8999         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9000             if(el != row){
9001                 return;
9002             }
9003             
9004             rowIndex = index;
9005         });
9006         
9007         return rowIndex;
9008     },
9009      /**
9010      * Returns the grid's underlying element = used by panel.Grid
9011      * @return {Element} The element
9012      */
9013     getGridEl : function(){
9014         return this.el;
9015     },
9016      /**
9017      * Forces a resize - used by panel.Grid
9018      * @return {Element} The element
9019      */
9020     autoSize : function()
9021     {
9022         //var ctr = Roo.get(this.container.dom.parentElement);
9023         var ctr = Roo.get(this.el.dom);
9024         
9025         var thd = this.getGridEl().select('thead',true).first();
9026         var tbd = this.getGridEl().select('tbody', true).first();
9027         var tfd = this.getGridEl().select('tfoot', true).first();
9028         
9029         var cw = ctr.getWidth();
9030         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9031         
9032         if (tbd) {
9033             
9034             tbd.setWidth(ctr.getWidth());
9035             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9036             // this needs fixing for various usage - currently only hydra job advers I think..
9037             //tdb.setHeight(
9038             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9039             //); 
9040             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9041             cw -= barsize;
9042         }
9043         cw = Math.max(cw, this.totalWidth);
9044         this.getGridEl().select('tbody tr',true).setWidth(cw);
9045         
9046         // resize 'expandable coloumn?
9047         
9048         return; // we doe not have a view in this design..
9049         
9050     },
9051     onBodyScroll: function()
9052     {
9053         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9054         if(this.mainHead){
9055             this.mainHead.setStyle({
9056                 'position' : 'relative',
9057                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9058             });
9059         }
9060         
9061         if(this.lazyLoad){
9062             
9063             var scrollHeight = this.mainBody.dom.scrollHeight;
9064             
9065             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9066             
9067             var height = this.mainBody.getHeight();
9068             
9069             if(scrollHeight - height == scrollTop) {
9070                 
9071                 var total = this.ds.getTotalCount();
9072                 
9073                 if(this.footer.cursor + this.footer.pageSize < total){
9074                     
9075                     this.footer.ds.load({
9076                         params : {
9077                             start : this.footer.cursor + this.footer.pageSize,
9078                             limit : this.footer.pageSize
9079                         },
9080                         add : true
9081                     });
9082                 }
9083             }
9084             
9085         }
9086     },
9087     
9088     onHeaderChange : function()
9089     {
9090         var header = this.renderHeader();
9091         var table = this.el.select('table', true).first();
9092         
9093         this.mainHead.remove();
9094         this.mainHead = table.createChild(header, this.mainBody, false);
9095     },
9096     
9097     onHiddenChange : function(colModel, colIndex, hidden)
9098     {
9099         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9100         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9101         
9102         this.CSS.updateRule(thSelector, "display", "");
9103         this.CSS.updateRule(tdSelector, "display", "");
9104         
9105         if(hidden){
9106             this.CSS.updateRule(thSelector, "display", "none");
9107             this.CSS.updateRule(tdSelector, "display", "none");
9108         }
9109         
9110         this.onHeaderChange();
9111         this.onLoad();
9112     },
9113     
9114     setColumnWidth: function(col_index, width)
9115     {
9116         // width = "md-2 xs-2..."
9117         if(!this.colModel.config[col_index]) {
9118             return;
9119         }
9120         
9121         var w = width.split(" ");
9122         
9123         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9124         
9125         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9126         
9127         
9128         for(var j = 0; j < w.length; j++) {
9129             
9130             if(!w[j]) {
9131                 continue;
9132             }
9133             
9134             var size_cls = w[j].split("-");
9135             
9136             if(!Number.isInteger(size_cls[1] * 1)) {
9137                 continue;
9138             }
9139             
9140             if(!this.colModel.config[col_index][size_cls[0]]) {
9141                 continue;
9142             }
9143             
9144             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9145                 continue;
9146             }
9147             
9148             h_row[0].classList.replace(
9149                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9150                 "col-"+size_cls[0]+"-"+size_cls[1]
9151             );
9152             
9153             for(var i = 0; i < rows.length; i++) {
9154                 
9155                 var size_cls = w[j].split("-");
9156                 
9157                 if(!Number.isInteger(size_cls[1] * 1)) {
9158                     continue;
9159                 }
9160                 
9161                 if(!this.colModel.config[col_index][size_cls[0]]) {
9162                     continue;
9163                 }
9164                 
9165                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9166                     continue;
9167                 }
9168                 
9169                 rows[i].classList.replace(
9170                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9171                     "col-"+size_cls[0]+"-"+size_cls[1]
9172                 );
9173             }
9174             
9175             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9176         }
9177     }
9178 });
9179
9180  
9181
9182  /*
9183  * - LGPL
9184  *
9185  * table cell
9186  * 
9187  */
9188
9189 /**
9190  * @class Roo.bootstrap.TableCell
9191  * @extends Roo.bootstrap.Component
9192  * Bootstrap TableCell class
9193  * @cfg {String} html cell contain text
9194  * @cfg {String} cls cell class
9195  * @cfg {String} tag cell tag (td|th) default td
9196  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9197  * @cfg {String} align Aligns the content in a cell
9198  * @cfg {String} axis Categorizes cells
9199  * @cfg {String} bgcolor Specifies the background color of a cell
9200  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9201  * @cfg {Number} colspan Specifies the number of columns a cell should span
9202  * @cfg {String} headers Specifies one or more header cells a cell is related to
9203  * @cfg {Number} height Sets the height of a cell
9204  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9205  * @cfg {Number} rowspan Sets the number of rows a cell should span
9206  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9207  * @cfg {String} valign Vertical aligns the content in a cell
9208  * @cfg {Number} width Specifies the width of a cell
9209  * 
9210  * @constructor
9211  * Create a new TableCell
9212  * @param {Object} config The config object
9213  */
9214
9215 Roo.bootstrap.TableCell = function(config){
9216     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9217 };
9218
9219 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9220     
9221     html: false,
9222     cls: false,
9223     tag: false,
9224     abbr: false,
9225     align: false,
9226     axis: false,
9227     bgcolor: false,
9228     charoff: false,
9229     colspan: false,
9230     headers: false,
9231     height: false,
9232     nowrap: false,
9233     rowspan: false,
9234     scope: false,
9235     valign: false,
9236     width: false,
9237     
9238     
9239     getAutoCreate : function(){
9240         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9241         
9242         cfg = {
9243             tag: 'td'
9244         };
9245         
9246         if(this.tag){
9247             cfg.tag = this.tag;
9248         }
9249         
9250         if (this.html) {
9251             cfg.html=this.html
9252         }
9253         if (this.cls) {
9254             cfg.cls=this.cls
9255         }
9256         if (this.abbr) {
9257             cfg.abbr=this.abbr
9258         }
9259         if (this.align) {
9260             cfg.align=this.align
9261         }
9262         if (this.axis) {
9263             cfg.axis=this.axis
9264         }
9265         if (this.bgcolor) {
9266             cfg.bgcolor=this.bgcolor
9267         }
9268         if (this.charoff) {
9269             cfg.charoff=this.charoff
9270         }
9271         if (this.colspan) {
9272             cfg.colspan=this.colspan
9273         }
9274         if (this.headers) {
9275             cfg.headers=this.headers
9276         }
9277         if (this.height) {
9278             cfg.height=this.height
9279         }
9280         if (this.nowrap) {
9281             cfg.nowrap=this.nowrap
9282         }
9283         if (this.rowspan) {
9284             cfg.rowspan=this.rowspan
9285         }
9286         if (this.scope) {
9287             cfg.scope=this.scope
9288         }
9289         if (this.valign) {
9290             cfg.valign=this.valign
9291         }
9292         if (this.width) {
9293             cfg.width=this.width
9294         }
9295         
9296         
9297         return cfg;
9298     }
9299    
9300 });
9301
9302  
9303
9304  /*
9305  * - LGPL
9306  *
9307  * table row
9308  * 
9309  */
9310
9311 /**
9312  * @class Roo.bootstrap.TableRow
9313  * @extends Roo.bootstrap.Component
9314  * Bootstrap TableRow class
9315  * @cfg {String} cls row class
9316  * @cfg {String} align Aligns the content in a table row
9317  * @cfg {String} bgcolor Specifies a background color for a table row
9318  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9319  * @cfg {String} valign Vertical aligns the content in a table row
9320  * 
9321  * @constructor
9322  * Create a new TableRow
9323  * @param {Object} config The config object
9324  */
9325
9326 Roo.bootstrap.TableRow = function(config){
9327     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9328 };
9329
9330 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9331     
9332     cls: false,
9333     align: false,
9334     bgcolor: false,
9335     charoff: false,
9336     valign: false,
9337     
9338     getAutoCreate : function(){
9339         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9340         
9341         cfg = {
9342             tag: 'tr'
9343         };
9344             
9345         if(this.cls){
9346             cfg.cls = this.cls;
9347         }
9348         if(this.align){
9349             cfg.align = this.align;
9350         }
9351         if(this.bgcolor){
9352             cfg.bgcolor = this.bgcolor;
9353         }
9354         if(this.charoff){
9355             cfg.charoff = this.charoff;
9356         }
9357         if(this.valign){
9358             cfg.valign = this.valign;
9359         }
9360         
9361         return cfg;
9362     }
9363    
9364 });
9365
9366  
9367
9368  /*
9369  * - LGPL
9370  *
9371  * table body
9372  * 
9373  */
9374
9375 /**
9376  * @class Roo.bootstrap.TableBody
9377  * @extends Roo.bootstrap.Component
9378  * Bootstrap TableBody class
9379  * @cfg {String} cls element class
9380  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9381  * @cfg {String} align Aligns the content inside the element
9382  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9383  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9384  * 
9385  * @constructor
9386  * Create a new TableBody
9387  * @param {Object} config The config object
9388  */
9389
9390 Roo.bootstrap.TableBody = function(config){
9391     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9392 };
9393
9394 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9395     
9396     cls: false,
9397     tag: false,
9398     align: false,
9399     charoff: false,
9400     valign: false,
9401     
9402     getAutoCreate : function(){
9403         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9404         
9405         cfg = {
9406             tag: 'tbody'
9407         };
9408             
9409         if (this.cls) {
9410             cfg.cls=this.cls
9411         }
9412         if(this.tag){
9413             cfg.tag = this.tag;
9414         }
9415         
9416         if(this.align){
9417             cfg.align = this.align;
9418         }
9419         if(this.charoff){
9420             cfg.charoff = this.charoff;
9421         }
9422         if(this.valign){
9423             cfg.valign = this.valign;
9424         }
9425         
9426         return cfg;
9427     }
9428     
9429     
9430 //    initEvents : function()
9431 //    {
9432 //        
9433 //        if(!this.store){
9434 //            return;
9435 //        }
9436 //        
9437 //        this.store = Roo.factory(this.store, Roo.data);
9438 //        this.store.on('load', this.onLoad, this);
9439 //        
9440 //        this.store.load();
9441 //        
9442 //    },
9443 //    
9444 //    onLoad: function () 
9445 //    {   
9446 //        this.fireEvent('load', this);
9447 //    }
9448 //    
9449 //   
9450 });
9451
9452  
9453
9454  /*
9455  * Based on:
9456  * Ext JS Library 1.1.1
9457  * Copyright(c) 2006-2007, Ext JS, LLC.
9458  *
9459  * Originally Released Under LGPL - original licence link has changed is not relivant.
9460  *
9461  * Fork - LGPL
9462  * <script type="text/javascript">
9463  */
9464
9465 // as we use this in bootstrap.
9466 Roo.namespace('Roo.form');
9467  /**
9468  * @class Roo.form.Action
9469  * Internal Class used to handle form actions
9470  * @constructor
9471  * @param {Roo.form.BasicForm} el The form element or its id
9472  * @param {Object} config Configuration options
9473  */
9474
9475  
9476  
9477 // define the action interface
9478 Roo.form.Action = function(form, options){
9479     this.form = form;
9480     this.options = options || {};
9481 };
9482 /**
9483  * Client Validation Failed
9484  * @const 
9485  */
9486 Roo.form.Action.CLIENT_INVALID = 'client';
9487 /**
9488  * Server Validation Failed
9489  * @const 
9490  */
9491 Roo.form.Action.SERVER_INVALID = 'server';
9492  /**
9493  * Connect to Server Failed
9494  * @const 
9495  */
9496 Roo.form.Action.CONNECT_FAILURE = 'connect';
9497 /**
9498  * Reading Data from Server Failed
9499  * @const 
9500  */
9501 Roo.form.Action.LOAD_FAILURE = 'load';
9502
9503 Roo.form.Action.prototype = {
9504     type : 'default',
9505     failureType : undefined,
9506     response : undefined,
9507     result : undefined,
9508
9509     // interface method
9510     run : function(options){
9511
9512     },
9513
9514     // interface method
9515     success : function(response){
9516
9517     },
9518
9519     // interface method
9520     handleResponse : function(response){
9521
9522     },
9523
9524     // default connection failure
9525     failure : function(response){
9526         
9527         this.response = response;
9528         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9529         this.form.afterAction(this, false);
9530     },
9531
9532     processResponse : function(response){
9533         this.response = response;
9534         if(!response.responseText){
9535             return true;
9536         }
9537         this.result = this.handleResponse(response);
9538         return this.result;
9539     },
9540
9541     // utility functions used internally
9542     getUrl : function(appendParams){
9543         var url = this.options.url || this.form.url || this.form.el.dom.action;
9544         if(appendParams){
9545             var p = this.getParams();
9546             if(p){
9547                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9548             }
9549         }
9550         return url;
9551     },
9552
9553     getMethod : function(){
9554         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9555     },
9556
9557     getParams : function(){
9558         var bp = this.form.baseParams;
9559         var p = this.options.params;
9560         if(p){
9561             if(typeof p == "object"){
9562                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9563             }else if(typeof p == 'string' && bp){
9564                 p += '&' + Roo.urlEncode(bp);
9565             }
9566         }else if(bp){
9567             p = Roo.urlEncode(bp);
9568         }
9569         return p;
9570     },
9571
9572     createCallback : function(){
9573         return {
9574             success: this.success,
9575             failure: this.failure,
9576             scope: this,
9577             timeout: (this.form.timeout*1000),
9578             upload: this.form.fileUpload ? this.success : undefined
9579         };
9580     }
9581 };
9582
9583 Roo.form.Action.Submit = function(form, options){
9584     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9585 };
9586
9587 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9588     type : 'submit',
9589
9590     haveProgress : false,
9591     uploadComplete : false,
9592     
9593     // uploadProgress indicator.
9594     uploadProgress : function()
9595     {
9596         if (!this.form.progressUrl) {
9597             return;
9598         }
9599         
9600         if (!this.haveProgress) {
9601             Roo.MessageBox.progress("Uploading", "Uploading");
9602         }
9603         if (this.uploadComplete) {
9604            Roo.MessageBox.hide();
9605            return;
9606         }
9607         
9608         this.haveProgress = true;
9609    
9610         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9611         
9612         var c = new Roo.data.Connection();
9613         c.request({
9614             url : this.form.progressUrl,
9615             params: {
9616                 id : uid
9617             },
9618             method: 'GET',
9619             success : function(req){
9620                //console.log(data);
9621                 var rdata = false;
9622                 var edata;
9623                 try  {
9624                    rdata = Roo.decode(req.responseText)
9625                 } catch (e) {
9626                     Roo.log("Invalid data from server..");
9627                     Roo.log(edata);
9628                     return;
9629                 }
9630                 if (!rdata || !rdata.success) {
9631                     Roo.log(rdata);
9632                     Roo.MessageBox.alert(Roo.encode(rdata));
9633                     return;
9634                 }
9635                 var data = rdata.data;
9636                 
9637                 if (this.uploadComplete) {
9638                    Roo.MessageBox.hide();
9639                    return;
9640                 }
9641                    
9642                 if (data){
9643                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9644                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9645                     );
9646                 }
9647                 this.uploadProgress.defer(2000,this);
9648             },
9649        
9650             failure: function(data) {
9651                 Roo.log('progress url failed ');
9652                 Roo.log(data);
9653             },
9654             scope : this
9655         });
9656            
9657     },
9658     
9659     
9660     run : function()
9661     {
9662         // run get Values on the form, so it syncs any secondary forms.
9663         this.form.getValues();
9664         
9665         var o = this.options;
9666         var method = this.getMethod();
9667         var isPost = method == 'POST';
9668         if(o.clientValidation === false || this.form.isValid()){
9669             
9670             if (this.form.progressUrl) {
9671                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9672                     (new Date() * 1) + '' + Math.random());
9673                     
9674             } 
9675             
9676             
9677             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9678                 form:this.form.el.dom,
9679                 url:this.getUrl(!isPost),
9680                 method: method,
9681                 params:isPost ? this.getParams() : null,
9682                 isUpload: this.form.fileUpload,
9683                 formData : this.form.formData
9684             }));
9685             
9686             this.uploadProgress();
9687
9688         }else if (o.clientValidation !== false){ // client validation failed
9689             this.failureType = Roo.form.Action.CLIENT_INVALID;
9690             this.form.afterAction(this, false);
9691         }
9692     },
9693
9694     success : function(response)
9695     {
9696         this.uploadComplete= true;
9697         if (this.haveProgress) {
9698             Roo.MessageBox.hide();
9699         }
9700         
9701         
9702         var result = this.processResponse(response);
9703         if(result === true || result.success){
9704             this.form.afterAction(this, true);
9705             return;
9706         }
9707         if(result.errors){
9708             this.form.markInvalid(result.errors);
9709             this.failureType = Roo.form.Action.SERVER_INVALID;
9710         }
9711         this.form.afterAction(this, false);
9712     },
9713     failure : function(response)
9714     {
9715         this.uploadComplete= true;
9716         if (this.haveProgress) {
9717             Roo.MessageBox.hide();
9718         }
9719         
9720         this.response = response;
9721         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9722         this.form.afterAction(this, false);
9723     },
9724     
9725     handleResponse : function(response){
9726         if(this.form.errorReader){
9727             var rs = this.form.errorReader.read(response);
9728             var errors = [];
9729             if(rs.records){
9730                 for(var i = 0, len = rs.records.length; i < len; i++) {
9731                     var r = rs.records[i];
9732                     errors[i] = r.data;
9733                 }
9734             }
9735             if(errors.length < 1){
9736                 errors = null;
9737             }
9738             return {
9739                 success : rs.success,
9740                 errors : errors
9741             };
9742         }
9743         var ret = false;
9744         try {
9745             ret = Roo.decode(response.responseText);
9746         } catch (e) {
9747             ret = {
9748                 success: false,
9749                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9750                 errors : []
9751             };
9752         }
9753         return ret;
9754         
9755     }
9756 });
9757
9758
9759 Roo.form.Action.Load = function(form, options){
9760     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9761     this.reader = this.form.reader;
9762 };
9763
9764 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9765     type : 'load',
9766
9767     run : function(){
9768         
9769         Roo.Ajax.request(Roo.apply(
9770                 this.createCallback(), {
9771                     method:this.getMethod(),
9772                     url:this.getUrl(false),
9773                     params:this.getParams()
9774         }));
9775     },
9776
9777     success : function(response){
9778         
9779         var result = this.processResponse(response);
9780         if(result === true || !result.success || !result.data){
9781             this.failureType = Roo.form.Action.LOAD_FAILURE;
9782             this.form.afterAction(this, false);
9783             return;
9784         }
9785         this.form.clearInvalid();
9786         this.form.setValues(result.data);
9787         this.form.afterAction(this, true);
9788     },
9789
9790     handleResponse : function(response){
9791         if(this.form.reader){
9792             var rs = this.form.reader.read(response);
9793             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9794             return {
9795                 success : rs.success,
9796                 data : data
9797             };
9798         }
9799         return Roo.decode(response.responseText);
9800     }
9801 });
9802
9803 Roo.form.Action.ACTION_TYPES = {
9804     'load' : Roo.form.Action.Load,
9805     'submit' : Roo.form.Action.Submit
9806 };/*
9807  * - LGPL
9808  *
9809  * form
9810  *
9811  */
9812
9813 /**
9814  * @class Roo.bootstrap.Form
9815  * @extends Roo.bootstrap.Component
9816  * Bootstrap Form class
9817  * @cfg {String} method  GET | POST (default POST)
9818  * @cfg {String} labelAlign top | left (default top)
9819  * @cfg {String} align left  | right - for navbars
9820  * @cfg {Boolean} loadMask load mask when submit (default true)
9821
9822  *
9823  * @constructor
9824  * Create a new Form
9825  * @param {Object} config The config object
9826  */
9827
9828
9829 Roo.bootstrap.Form = function(config){
9830     
9831     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9832     
9833     Roo.bootstrap.Form.popover.apply();
9834     
9835     this.addEvents({
9836         /**
9837          * @event clientvalidation
9838          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9839          * @param {Form} this
9840          * @param {Boolean} valid true if the form has passed client-side validation
9841          */
9842         clientvalidation: true,
9843         /**
9844          * @event beforeaction
9845          * Fires before any action is performed. Return false to cancel the action.
9846          * @param {Form} this
9847          * @param {Action} action The action to be performed
9848          */
9849         beforeaction: true,
9850         /**
9851          * @event actionfailed
9852          * Fires when an action fails.
9853          * @param {Form} this
9854          * @param {Action} action The action that failed
9855          */
9856         actionfailed : true,
9857         /**
9858          * @event actioncomplete
9859          * Fires when an action is completed.
9860          * @param {Form} this
9861          * @param {Action} action The action that completed
9862          */
9863         actioncomplete : true
9864     });
9865 };
9866
9867 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9868
9869      /**
9870      * @cfg {String} method
9871      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9872      */
9873     method : 'POST',
9874     /**
9875      * @cfg {String} url
9876      * The URL to use for form actions if one isn't supplied in the action options.
9877      */
9878     /**
9879      * @cfg {Boolean} fileUpload
9880      * Set to true if this form is a file upload.
9881      */
9882
9883     /**
9884      * @cfg {Object} baseParams
9885      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9886      */
9887
9888     /**
9889      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9890      */
9891     timeout: 30,
9892     /**
9893      * @cfg {Sting} align (left|right) for navbar forms
9894      */
9895     align : 'left',
9896
9897     // private
9898     activeAction : null,
9899
9900     /**
9901      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9902      * element by passing it or its id or mask the form itself by passing in true.
9903      * @type Mixed
9904      */
9905     waitMsgTarget : false,
9906
9907     loadMask : true,
9908     
9909     /**
9910      * @cfg {Boolean} errorMask (true|false) default false
9911      */
9912     errorMask : false,
9913     
9914     /**
9915      * @cfg {Number} maskOffset Default 100
9916      */
9917     maskOffset : 100,
9918     
9919     /**
9920      * @cfg {Boolean} maskBody
9921      */
9922     maskBody : false,
9923
9924     getAutoCreate : function(){
9925
9926         var cfg = {
9927             tag: 'form',
9928             method : this.method || 'POST',
9929             id : this.id || Roo.id(),
9930             cls : ''
9931         };
9932         if (this.parent().xtype.match(/^Nav/)) {
9933             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9934
9935         }
9936
9937         if (this.labelAlign == 'left' ) {
9938             cfg.cls += ' form-horizontal';
9939         }
9940
9941
9942         return cfg;
9943     },
9944     initEvents : function()
9945     {
9946         this.el.on('submit', this.onSubmit, this);
9947         // this was added as random key presses on the form where triggering form submit.
9948         this.el.on('keypress', function(e) {
9949             if (e.getCharCode() != 13) {
9950                 return true;
9951             }
9952             // we might need to allow it for textareas.. and some other items.
9953             // check e.getTarget().
9954
9955             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9956                 return true;
9957             }
9958
9959             Roo.log("keypress blocked");
9960
9961             e.preventDefault();
9962             return false;
9963         });
9964         
9965     },
9966     // private
9967     onSubmit : function(e){
9968         e.stopEvent();
9969     },
9970
9971      /**
9972      * Returns true if client-side validation on the form is successful.
9973      * @return Boolean
9974      */
9975     isValid : function(){
9976         var items = this.getItems();
9977         var valid = true;
9978         var target = false;
9979         
9980         items.each(function(f){
9981             
9982             if(f.validate()){
9983                 return;
9984             }
9985             
9986             Roo.log('invalid field: ' + f.name);
9987             
9988             valid = false;
9989
9990             if(!target && f.el.isVisible(true)){
9991                 target = f;
9992             }
9993            
9994         });
9995         
9996         if(this.errorMask && !valid){
9997             Roo.bootstrap.Form.popover.mask(this, target);
9998         }
9999         
10000         return valid;
10001     },
10002     
10003     /**
10004      * Returns true if any fields in this form have changed since their original load.
10005      * @return Boolean
10006      */
10007     isDirty : function(){
10008         var dirty = false;
10009         var items = this.getItems();
10010         items.each(function(f){
10011            if(f.isDirty()){
10012                dirty = true;
10013                return false;
10014            }
10015            return true;
10016         });
10017         return dirty;
10018     },
10019      /**
10020      * Performs a predefined action (submit or load) or custom actions you define on this form.
10021      * @param {String} actionName The name of the action type
10022      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10023      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10024      * accept other config options):
10025      * <pre>
10026 Property          Type             Description
10027 ----------------  ---------------  ----------------------------------------------------------------------------------
10028 url               String           The url for the action (defaults to the form's url)
10029 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10030 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10031 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10032                                    validate the form on the client (defaults to false)
10033      * </pre>
10034      * @return {BasicForm} this
10035      */
10036     doAction : function(action, options){
10037         if(typeof action == 'string'){
10038             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10039         }
10040         if(this.fireEvent('beforeaction', this, action) !== false){
10041             this.beforeAction(action);
10042             action.run.defer(100, action);
10043         }
10044         return this;
10045     },
10046
10047     // private
10048     beforeAction : function(action){
10049         var o = action.options;
10050         
10051         if(this.loadMask){
10052             
10053             if(this.maskBody){
10054                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10055             } else {
10056                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10057             }
10058         }
10059         // not really supported yet.. ??
10060
10061         //if(this.waitMsgTarget === true){
10062         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10063         //}else if(this.waitMsgTarget){
10064         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10065         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10066         //}else {
10067         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10068        // }
10069
10070     },
10071
10072     // private
10073     afterAction : function(action, success){
10074         this.activeAction = null;
10075         var o = action.options;
10076
10077         if(this.loadMask){
10078             
10079             if(this.maskBody){
10080                 Roo.get(document.body).unmask();
10081             } else {
10082                 this.el.unmask();
10083             }
10084         }
10085         
10086         //if(this.waitMsgTarget === true){
10087 //            this.el.unmask();
10088         //}else if(this.waitMsgTarget){
10089         //    this.waitMsgTarget.unmask();
10090         //}else{
10091         //    Roo.MessageBox.updateProgress(1);
10092         //    Roo.MessageBox.hide();
10093        // }
10094         //
10095         if(success){
10096             if(o.reset){
10097                 this.reset();
10098             }
10099             Roo.callback(o.success, o.scope, [this, action]);
10100             this.fireEvent('actioncomplete', this, action);
10101
10102         }else{
10103
10104             // failure condition..
10105             // we have a scenario where updates need confirming.
10106             // eg. if a locking scenario exists..
10107             // we look for { errors : { needs_confirm : true }} in the response.
10108             if (
10109                 (typeof(action.result) != 'undefined')  &&
10110                 (typeof(action.result.errors) != 'undefined')  &&
10111                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10112            ){
10113                 var _t = this;
10114                 Roo.log("not supported yet");
10115                  /*
10116
10117                 Roo.MessageBox.confirm(
10118                     "Change requires confirmation",
10119                     action.result.errorMsg,
10120                     function(r) {
10121                         if (r != 'yes') {
10122                             return;
10123                         }
10124                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10125                     }
10126
10127                 );
10128                 */
10129
10130
10131                 return;
10132             }
10133
10134             Roo.callback(o.failure, o.scope, [this, action]);
10135             // show an error message if no failed handler is set..
10136             if (!this.hasListener('actionfailed')) {
10137                 Roo.log("need to add dialog support");
10138                 /*
10139                 Roo.MessageBox.alert("Error",
10140                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10141                         action.result.errorMsg :
10142                         "Saving Failed, please check your entries or try again"
10143                 );
10144                 */
10145             }
10146
10147             this.fireEvent('actionfailed', this, action);
10148         }
10149
10150     },
10151     /**
10152      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10153      * @param {String} id The value to search for
10154      * @return Field
10155      */
10156     findField : function(id){
10157         var items = this.getItems();
10158         var field = items.get(id);
10159         if(!field){
10160              items.each(function(f){
10161                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10162                     field = f;
10163                     return false;
10164                 }
10165                 return true;
10166             });
10167         }
10168         return field || null;
10169     },
10170      /**
10171      * Mark fields in this form invalid in bulk.
10172      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10173      * @return {BasicForm} this
10174      */
10175     markInvalid : function(errors){
10176         if(errors instanceof Array){
10177             for(var i = 0, len = errors.length; i < len; i++){
10178                 var fieldError = errors[i];
10179                 var f = this.findField(fieldError.id);
10180                 if(f){
10181                     f.markInvalid(fieldError.msg);
10182                 }
10183             }
10184         }else{
10185             var field, id;
10186             for(id in errors){
10187                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10188                     field.markInvalid(errors[id]);
10189                 }
10190             }
10191         }
10192         //Roo.each(this.childForms || [], function (f) {
10193         //    f.markInvalid(errors);
10194         //});
10195
10196         return this;
10197     },
10198
10199     /**
10200      * Set values for fields in this form in bulk.
10201      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10202      * @return {BasicForm} this
10203      */
10204     setValues : function(values){
10205         if(values instanceof Array){ // array of objects
10206             for(var i = 0, len = values.length; i < len; i++){
10207                 var v = values[i];
10208                 var f = this.findField(v.id);
10209                 if(f){
10210                     f.setValue(v.value);
10211                     if(this.trackResetOnLoad){
10212                         f.originalValue = f.getValue();
10213                     }
10214                 }
10215             }
10216         }else{ // object hash
10217             var field, id;
10218             for(id in values){
10219                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10220
10221                     if (field.setFromData &&
10222                         field.valueField &&
10223                         field.displayField &&
10224                         // combos' with local stores can
10225                         // be queried via setValue()
10226                         // to set their value..
10227                         (field.store && !field.store.isLocal)
10228                         ) {
10229                         // it's a combo
10230                         var sd = { };
10231                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10232                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10233                         field.setFromData(sd);
10234
10235                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10236                         
10237                         field.setFromData(values);
10238                         
10239                     } else {
10240                         field.setValue(values[id]);
10241                     }
10242
10243
10244                     if(this.trackResetOnLoad){
10245                         field.originalValue = field.getValue();
10246                     }
10247                 }
10248             }
10249         }
10250
10251         //Roo.each(this.childForms || [], function (f) {
10252         //    f.setValues(values);
10253         //});
10254
10255         return this;
10256     },
10257
10258     /**
10259      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10260      * they are returned as an array.
10261      * @param {Boolean} asString
10262      * @return {Object}
10263      */
10264     getValues : function(asString){
10265         //if (this.childForms) {
10266             // copy values from the child forms
10267         //    Roo.each(this.childForms, function (f) {
10268         //        this.setValues(f.getValues());
10269         //    }, this);
10270         //}
10271
10272
10273
10274         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10275         if(asString === true){
10276             return fs;
10277         }
10278         return Roo.urlDecode(fs);
10279     },
10280
10281     /**
10282      * Returns the fields in this form as an object with key/value pairs.
10283      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10284      * @return {Object}
10285      */
10286     getFieldValues : function(with_hidden)
10287     {
10288         var items = this.getItems();
10289         var ret = {};
10290         items.each(function(f){
10291             
10292             if (!f.getName()) {
10293                 return;
10294             }
10295             
10296             var v = f.getValue();
10297             
10298             if (f.inputType =='radio') {
10299                 if (typeof(ret[f.getName()]) == 'undefined') {
10300                     ret[f.getName()] = ''; // empty..
10301                 }
10302
10303                 if (!f.el.dom.checked) {
10304                     return;
10305
10306                 }
10307                 v = f.el.dom.value;
10308
10309             }
10310             
10311             if(f.xtype == 'MoneyField'){
10312                 ret[f.currencyName] = f.getCurrency();
10313             }
10314
10315             // not sure if this supported any more..
10316             if ((typeof(v) == 'object') && f.getRawValue) {
10317                 v = f.getRawValue() ; // dates..
10318             }
10319             // combo boxes where name != hiddenName...
10320             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10321                 ret[f.name] = f.getRawValue();
10322             }
10323             ret[f.getName()] = v;
10324         });
10325
10326         return ret;
10327     },
10328
10329     /**
10330      * Clears all invalid messages in this form.
10331      * @return {BasicForm} this
10332      */
10333     clearInvalid : function(){
10334         var items = this.getItems();
10335
10336         items.each(function(f){
10337            f.clearInvalid();
10338         });
10339
10340         return this;
10341     },
10342
10343     /**
10344      * Resets this form.
10345      * @return {BasicForm} this
10346      */
10347     reset : function(){
10348         var items = this.getItems();
10349         items.each(function(f){
10350             f.reset();
10351         });
10352
10353         Roo.each(this.childForms || [], function (f) {
10354             f.reset();
10355         });
10356
10357
10358         return this;
10359     },
10360     
10361     getItems : function()
10362     {
10363         var r=new Roo.util.MixedCollection(false, function(o){
10364             return o.id || (o.id = Roo.id());
10365         });
10366         var iter = function(el) {
10367             if (el.inputEl) {
10368                 r.add(el);
10369             }
10370             if (!el.items) {
10371                 return;
10372             }
10373             Roo.each(el.items,function(e) {
10374                 iter(e);
10375             });
10376         };
10377
10378         iter(this);
10379         return r;
10380     },
10381     
10382     hideFields : function(items)
10383     {
10384         Roo.each(items, function(i){
10385             
10386             var f = this.findField(i);
10387             
10388             if(!f){
10389                 return;
10390             }
10391             
10392             f.hide();
10393             
10394         }, this);
10395     },
10396     
10397     showFields : function(items)
10398     {
10399         Roo.each(items, function(i){
10400             
10401             var f = this.findField(i);
10402             
10403             if(!f){
10404                 return;
10405             }
10406             
10407             f.show();
10408             
10409         }, this);
10410     }
10411
10412 });
10413
10414 Roo.apply(Roo.bootstrap.Form, {
10415     
10416     popover : {
10417         
10418         padding : 5,
10419         
10420         isApplied : false,
10421         
10422         isMasked : false,
10423         
10424         form : false,
10425         
10426         target : false,
10427         
10428         toolTip : false,
10429         
10430         intervalID : false,
10431         
10432         maskEl : false,
10433         
10434         apply : function()
10435         {
10436             if(this.isApplied){
10437                 return;
10438             }
10439             
10440             this.maskEl = {
10441                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10442                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10443                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10444                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10445             };
10446             
10447             this.maskEl.top.enableDisplayMode("block");
10448             this.maskEl.left.enableDisplayMode("block");
10449             this.maskEl.bottom.enableDisplayMode("block");
10450             this.maskEl.right.enableDisplayMode("block");
10451             
10452             this.toolTip = new Roo.bootstrap.Tooltip({
10453                 cls : 'roo-form-error-popover',
10454                 alignment : {
10455                     'left' : ['r-l', [-2,0], 'right'],
10456                     'right' : ['l-r', [2,0], 'left'],
10457                     'bottom' : ['tl-bl', [0,2], 'top'],
10458                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10459                 }
10460             });
10461             
10462             this.toolTip.render(Roo.get(document.body));
10463
10464             this.toolTip.el.enableDisplayMode("block");
10465             
10466             Roo.get(document.body).on('click', function(){
10467                 this.unmask();
10468             }, this);
10469             
10470             Roo.get(document.body).on('touchstart', function(){
10471                 this.unmask();
10472             }, this);
10473             
10474             this.isApplied = true
10475         },
10476         
10477         mask : function(form, target)
10478         {
10479             this.form = form;
10480             
10481             this.target = target;
10482             
10483             if(!this.form.errorMask || !target.el){
10484                 return;
10485             }
10486             
10487             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10488             
10489             Roo.log(scrollable);
10490             
10491             var ot = this.target.el.calcOffsetsTo(scrollable);
10492             
10493             var scrollTo = ot[1] - this.form.maskOffset;
10494             
10495             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10496             
10497             scrollable.scrollTo('top', scrollTo);
10498             
10499             var box = this.target.el.getBox();
10500             Roo.log(box);
10501             var zIndex = Roo.bootstrap.Modal.zIndex++;
10502
10503             
10504             this.maskEl.top.setStyle('position', 'absolute');
10505             this.maskEl.top.setStyle('z-index', zIndex);
10506             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10507             this.maskEl.top.setLeft(0);
10508             this.maskEl.top.setTop(0);
10509             this.maskEl.top.show();
10510             
10511             this.maskEl.left.setStyle('position', 'absolute');
10512             this.maskEl.left.setStyle('z-index', zIndex);
10513             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10514             this.maskEl.left.setLeft(0);
10515             this.maskEl.left.setTop(box.y - this.padding);
10516             this.maskEl.left.show();
10517
10518             this.maskEl.bottom.setStyle('position', 'absolute');
10519             this.maskEl.bottom.setStyle('z-index', zIndex);
10520             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10521             this.maskEl.bottom.setLeft(0);
10522             this.maskEl.bottom.setTop(box.bottom + this.padding);
10523             this.maskEl.bottom.show();
10524
10525             this.maskEl.right.setStyle('position', 'absolute');
10526             this.maskEl.right.setStyle('z-index', zIndex);
10527             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10528             this.maskEl.right.setLeft(box.right + this.padding);
10529             this.maskEl.right.setTop(box.y - this.padding);
10530             this.maskEl.right.show();
10531
10532             this.toolTip.bindEl = this.target.el;
10533
10534             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10535
10536             var tip = this.target.blankText;
10537
10538             if(this.target.getValue() !== '' ) {
10539                 
10540                 if (this.target.invalidText.length) {
10541                     tip = this.target.invalidText;
10542                 } else if (this.target.regexText.length){
10543                     tip = this.target.regexText;
10544                 }
10545             }
10546
10547             this.toolTip.show(tip);
10548
10549             this.intervalID = window.setInterval(function() {
10550                 Roo.bootstrap.Form.popover.unmask();
10551             }, 10000);
10552
10553             window.onwheel = function(){ return false;};
10554             
10555             (function(){ this.isMasked = true; }).defer(500, this);
10556             
10557         },
10558         
10559         unmask : function()
10560         {
10561             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10562                 return;
10563             }
10564             
10565             this.maskEl.top.setStyle('position', 'absolute');
10566             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10567             this.maskEl.top.hide();
10568
10569             this.maskEl.left.setStyle('position', 'absolute');
10570             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10571             this.maskEl.left.hide();
10572
10573             this.maskEl.bottom.setStyle('position', 'absolute');
10574             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10575             this.maskEl.bottom.hide();
10576
10577             this.maskEl.right.setStyle('position', 'absolute');
10578             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10579             this.maskEl.right.hide();
10580             
10581             this.toolTip.hide();
10582             
10583             this.toolTip.el.hide();
10584             
10585             window.onwheel = function(){ return true;};
10586             
10587             if(this.intervalID){
10588                 window.clearInterval(this.intervalID);
10589                 this.intervalID = false;
10590             }
10591             
10592             this.isMasked = false;
10593             
10594         }
10595         
10596     }
10597     
10598 });
10599
10600 /*
10601  * Based on:
10602  * Ext JS Library 1.1.1
10603  * Copyright(c) 2006-2007, Ext JS, LLC.
10604  *
10605  * Originally Released Under LGPL - original licence link has changed is not relivant.
10606  *
10607  * Fork - LGPL
10608  * <script type="text/javascript">
10609  */
10610 /**
10611  * @class Roo.form.VTypes
10612  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10613  * @singleton
10614  */
10615 Roo.form.VTypes = function(){
10616     // closure these in so they are only created once.
10617     var alpha = /^[a-zA-Z_]+$/;
10618     var alphanum = /^[a-zA-Z0-9_]+$/;
10619     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10620     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10621
10622     // All these messages and functions are configurable
10623     return {
10624         /**
10625          * The function used to validate email addresses
10626          * @param {String} value The email address
10627          */
10628         'email' : function(v){
10629             return email.test(v);
10630         },
10631         /**
10632          * The error text to display when the email validation function returns false
10633          * @type String
10634          */
10635         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10636         /**
10637          * The keystroke filter mask to be applied on email input
10638          * @type RegExp
10639          */
10640         'emailMask' : /[a-z0-9_\.\-@]/i,
10641
10642         /**
10643          * The function used to validate URLs
10644          * @param {String} value The URL
10645          */
10646         'url' : function(v){
10647             return url.test(v);
10648         },
10649         /**
10650          * The error text to display when the url validation function returns false
10651          * @type String
10652          */
10653         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10654         
10655         /**
10656          * The function used to validate alpha values
10657          * @param {String} value The value
10658          */
10659         'alpha' : function(v){
10660             return alpha.test(v);
10661         },
10662         /**
10663          * The error text to display when the alpha validation function returns false
10664          * @type String
10665          */
10666         'alphaText' : 'This field should only contain letters and _',
10667         /**
10668          * The keystroke filter mask to be applied on alpha input
10669          * @type RegExp
10670          */
10671         'alphaMask' : /[a-z_]/i,
10672
10673         /**
10674          * The function used to validate alphanumeric values
10675          * @param {String} value The value
10676          */
10677         'alphanum' : function(v){
10678             return alphanum.test(v);
10679         },
10680         /**
10681          * The error text to display when the alphanumeric validation function returns false
10682          * @type String
10683          */
10684         'alphanumText' : 'This field should only contain letters, numbers and _',
10685         /**
10686          * The keystroke filter mask to be applied on alphanumeric input
10687          * @type RegExp
10688          */
10689         'alphanumMask' : /[a-z0-9_]/i
10690     };
10691 }();/*
10692  * - LGPL
10693  *
10694  * Input
10695  * 
10696  */
10697
10698 /**
10699  * @class Roo.bootstrap.Input
10700  * @extends Roo.bootstrap.Component
10701  * Bootstrap Input class
10702  * @cfg {Boolean} disabled is it disabled
10703  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10704  * @cfg {String} name name of the input
10705  * @cfg {string} fieldLabel - the label associated
10706  * @cfg {string} placeholder - placeholder to put in text.
10707  * @cfg {string}  before - input group add on before
10708  * @cfg {string} after - input group add on after
10709  * @cfg {string} size - (lg|sm) or leave empty..
10710  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10711  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10712  * @cfg {Number} md colspan out of 12 for computer-sized screens
10713  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10714  * @cfg {string} value default value of the input
10715  * @cfg {Number} labelWidth set the width of label 
10716  * @cfg {Number} labellg set the width of label (1-12)
10717  * @cfg {Number} labelmd set the width of label (1-12)
10718  * @cfg {Number} labelsm set the width of label (1-12)
10719  * @cfg {Number} labelxs set the width of label (1-12)
10720  * @cfg {String} labelAlign (top|left)
10721  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10722  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10723  * @cfg {String} indicatorpos (left|right) default left
10724  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10725  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10726  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10727
10728  * @cfg {String} align (left|center|right) Default left
10729  * @cfg {Boolean} forceFeedback (true|false) Default false
10730  * 
10731  * @constructor
10732  * Create a new Input
10733  * @param {Object} config The config object
10734  */
10735
10736 Roo.bootstrap.Input = function(config){
10737     
10738     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10739     
10740     this.addEvents({
10741         /**
10742          * @event focus
10743          * Fires when this field receives input focus.
10744          * @param {Roo.form.Field} this
10745          */
10746         focus : true,
10747         /**
10748          * @event blur
10749          * Fires when this field loses input focus.
10750          * @param {Roo.form.Field} this
10751          */
10752         blur : true,
10753         /**
10754          * @event specialkey
10755          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10756          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10757          * @param {Roo.form.Field} this
10758          * @param {Roo.EventObject} e The event object
10759          */
10760         specialkey : true,
10761         /**
10762          * @event change
10763          * Fires just before the field blurs if the field value has changed.
10764          * @param {Roo.form.Field} this
10765          * @param {Mixed} newValue The new value
10766          * @param {Mixed} oldValue The original value
10767          */
10768         change : true,
10769         /**
10770          * @event invalid
10771          * Fires after the field has been marked as invalid.
10772          * @param {Roo.form.Field} this
10773          * @param {String} msg The validation message
10774          */
10775         invalid : true,
10776         /**
10777          * @event valid
10778          * Fires after the field has been validated with no errors.
10779          * @param {Roo.form.Field} this
10780          */
10781         valid : true,
10782          /**
10783          * @event keyup
10784          * Fires after the key up
10785          * @param {Roo.form.Field} this
10786          * @param {Roo.EventObject}  e The event Object
10787          */
10788         keyup : true,
10789         /**
10790          * @event paste
10791          * Fires after the user pastes into input
10792          * @param {Roo.form.Field} this
10793          * @param {Roo.EventObject}  e The event Object
10794          */
10795         paste : true
10796     });
10797 };
10798
10799 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10800      /**
10801      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10802       automatic validation (defaults to "keyup").
10803      */
10804     validationEvent : "keyup",
10805      /**
10806      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10807      */
10808     validateOnBlur : true,
10809     /**
10810      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10811      */
10812     validationDelay : 250,
10813      /**
10814      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10815      */
10816     focusClass : "x-form-focus",  // not needed???
10817     
10818        
10819     /**
10820      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10821      */
10822     invalidClass : "has-warning",
10823     
10824     /**
10825      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10826      */
10827     validClass : "has-success",
10828     
10829     /**
10830      * @cfg {Boolean} hasFeedback (true|false) default true
10831      */
10832     hasFeedback : true,
10833     
10834     /**
10835      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10836      */
10837     invalidFeedbackClass : "glyphicon-warning-sign",
10838     
10839     /**
10840      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10841      */
10842     validFeedbackClass : "glyphicon-ok",
10843     
10844     /**
10845      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10846      */
10847     selectOnFocus : false,
10848     
10849      /**
10850      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10851      */
10852     maskRe : null,
10853        /**
10854      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10855      */
10856     vtype : null,
10857     
10858       /**
10859      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10860      */
10861     disableKeyFilter : false,
10862     
10863        /**
10864      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10865      */
10866     disabled : false,
10867      /**
10868      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10869      */
10870     allowBlank : true,
10871     /**
10872      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10873      */
10874     blankText : "Please complete this mandatory field",
10875     
10876      /**
10877      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10878      */
10879     minLength : 0,
10880     /**
10881      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10882      */
10883     maxLength : Number.MAX_VALUE,
10884     /**
10885      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10886      */
10887     minLengthText : "The minimum length for this field is {0}",
10888     /**
10889      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10890      */
10891     maxLengthText : "The maximum length for this field is {0}",
10892   
10893     
10894     /**
10895      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10896      * If available, this function will be called only after the basic validators all return true, and will be passed the
10897      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10898      */
10899     validator : null,
10900     /**
10901      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10902      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10903      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10904      */
10905     regex : null,
10906     /**
10907      * @cfg {String} regexText -- Depricated - use Invalid Text
10908      */
10909     regexText : "",
10910     
10911     /**
10912      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10913      */
10914     invalidText : "",
10915     
10916     
10917     
10918     autocomplete: false,
10919     
10920     
10921     fieldLabel : '',
10922     inputType : 'text',
10923     
10924     name : false,
10925     placeholder: false,
10926     before : false,
10927     after : false,
10928     size : false,
10929     hasFocus : false,
10930     preventMark: false,
10931     isFormField : true,
10932     value : '',
10933     labelWidth : 2,
10934     labelAlign : false,
10935     readOnly : false,
10936     align : false,
10937     formatedValue : false,
10938     forceFeedback : false,
10939     
10940     indicatorpos : 'left',
10941     
10942     labellg : 0,
10943     labelmd : 0,
10944     labelsm : 0,
10945     labelxs : 0,
10946     
10947     capture : '',
10948     accept : '',
10949     
10950     parentLabelAlign : function()
10951     {
10952         var parent = this;
10953         while (parent.parent()) {
10954             parent = parent.parent();
10955             if (typeof(parent.labelAlign) !='undefined') {
10956                 return parent.labelAlign;
10957             }
10958         }
10959         return 'left';
10960         
10961     },
10962     
10963     getAutoCreate : function()
10964     {
10965         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10966         
10967         var id = Roo.id();
10968         
10969         var cfg = {};
10970         
10971         if(this.inputType != 'hidden'){
10972             cfg.cls = 'form-group' //input-group
10973         }
10974         
10975         var input =  {
10976             tag: 'input',
10977             id : id,
10978             type : this.inputType,
10979             value : this.value,
10980             cls : 'form-control',
10981             placeholder : this.placeholder || '',
10982             autocomplete : this.autocomplete || 'new-password'
10983         };
10984         if (this.inputType == 'file') {
10985             input.style = 'overflow:hidden'; // why not in CSS?
10986         }
10987         
10988         if(this.capture.length){
10989             input.capture = this.capture;
10990         }
10991         
10992         if(this.accept.length){
10993             input.accept = this.accept + "/*";
10994         }
10995         
10996         if(this.align){
10997             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10998         }
10999         
11000         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11001             input.maxLength = this.maxLength;
11002         }
11003         
11004         if (this.disabled) {
11005             input.disabled=true;
11006         }
11007         
11008         if (this.readOnly) {
11009             input.readonly=true;
11010         }
11011         
11012         if (this.name) {
11013             input.name = this.name;
11014         }
11015         
11016         if (this.size) {
11017             input.cls += ' input-' + this.size;
11018         }
11019         
11020         var settings=this;
11021         ['xs','sm','md','lg'].map(function(size){
11022             if (settings[size]) {
11023                 cfg.cls += ' col-' + size + '-' + settings[size];
11024             }
11025         });
11026         
11027         var inputblock = input;
11028         
11029         var feedback = {
11030             tag: 'span',
11031             cls: 'glyphicon form-control-feedback'
11032         };
11033             
11034         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11035             
11036             inputblock = {
11037                 cls : 'has-feedback',
11038                 cn :  [
11039                     input,
11040                     feedback
11041                 ] 
11042             };  
11043         }
11044         
11045         if (this.before || this.after) {
11046             
11047             inputblock = {
11048                 cls : 'input-group',
11049                 cn :  [] 
11050             };
11051             
11052             if (this.before && typeof(this.before) == 'string') {
11053                 
11054                 inputblock.cn.push({
11055                     tag :'span',
11056                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11057                     html : this.before
11058                 });
11059             }
11060             if (this.before && typeof(this.before) == 'object') {
11061                 this.before = Roo.factory(this.before);
11062                 
11063                 inputblock.cn.push({
11064                     tag :'span',
11065                     cls : 'roo-input-before input-group-prepend   input-group-' +
11066                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11067                 });
11068             }
11069             
11070             inputblock.cn.push(input);
11071             
11072             if (this.after && typeof(this.after) == 'string') {
11073                 inputblock.cn.push({
11074                     tag :'span',
11075                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11076                     html : this.after
11077                 });
11078             }
11079             if (this.after && typeof(this.after) == 'object') {
11080                 this.after = Roo.factory(this.after);
11081                 
11082                 inputblock.cn.push({
11083                     tag :'span',
11084                     cls : 'roo-input-after input-group-append  input-group-' +
11085                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11086                 });
11087             }
11088             
11089             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11090                 inputblock.cls += ' has-feedback';
11091                 inputblock.cn.push(feedback);
11092             }
11093         };
11094         var indicator = {
11095             tag : 'i',
11096             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11097             tooltip : 'This field is required'
11098         };
11099         if (this.allowBlank ) {
11100             indicator.style = this.allowBlank ? ' display:none' : '';
11101         }
11102         if (align ==='left' && this.fieldLabel.length) {
11103             
11104             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11105             
11106             cfg.cn = [
11107                 indicator,
11108                 {
11109                     tag: 'label',
11110                     'for' :  id,
11111                     cls : 'control-label col-form-label',
11112                     html : this.fieldLabel
11113
11114                 },
11115                 {
11116                     cls : "", 
11117                     cn: [
11118                         inputblock
11119                     ]
11120                 }
11121             ];
11122             
11123             var labelCfg = cfg.cn[1];
11124             var contentCfg = cfg.cn[2];
11125             
11126             if(this.indicatorpos == 'right'){
11127                 cfg.cn = [
11128                     {
11129                         tag: 'label',
11130                         'for' :  id,
11131                         cls : 'control-label col-form-label',
11132                         cn : [
11133                             {
11134                                 tag : 'span',
11135                                 html : this.fieldLabel
11136                             },
11137                             indicator
11138                         ]
11139                     },
11140                     {
11141                         cls : "",
11142                         cn: [
11143                             inputblock
11144                         ]
11145                     }
11146
11147                 ];
11148                 
11149                 labelCfg = cfg.cn[0];
11150                 contentCfg = cfg.cn[1];
11151             
11152             }
11153             
11154             if(this.labelWidth > 12){
11155                 labelCfg.style = "width: " + this.labelWidth + 'px';
11156             }
11157             
11158             if(this.labelWidth < 13 && this.labelmd == 0){
11159                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11160             }
11161             
11162             if(this.labellg > 0){
11163                 labelCfg.cls += ' col-lg-' + this.labellg;
11164                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11165             }
11166             
11167             if(this.labelmd > 0){
11168                 labelCfg.cls += ' col-md-' + this.labelmd;
11169                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11170             }
11171             
11172             if(this.labelsm > 0){
11173                 labelCfg.cls += ' col-sm-' + this.labelsm;
11174                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11175             }
11176             
11177             if(this.labelxs > 0){
11178                 labelCfg.cls += ' col-xs-' + this.labelxs;
11179                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11180             }
11181             
11182             
11183         } else if ( this.fieldLabel.length) {
11184                 
11185             
11186             
11187             cfg.cn = [
11188                 {
11189                     tag : 'i',
11190                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11191                     tooltip : 'This field is required',
11192                     style : this.allowBlank ? ' display:none' : '' 
11193                 },
11194                 {
11195                     tag: 'label',
11196                    //cls : 'input-group-addon',
11197                     html : this.fieldLabel
11198
11199                 },
11200
11201                inputblock
11202
11203            ];
11204            
11205            if(this.indicatorpos == 'right'){
11206        
11207                 cfg.cn = [
11208                     {
11209                         tag: 'label',
11210                        //cls : 'input-group-addon',
11211                         html : this.fieldLabel
11212
11213                     },
11214                     {
11215                         tag : 'i',
11216                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11217                         tooltip : 'This field is required',
11218                         style : this.allowBlank ? ' display:none' : '' 
11219                     },
11220
11221                    inputblock
11222
11223                ];
11224
11225             }
11226
11227         } else {
11228             
11229             cfg.cn = [
11230
11231                     inputblock
11232
11233             ];
11234                 
11235                 
11236         };
11237         
11238         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11239            cfg.cls += ' navbar-form';
11240         }
11241         
11242         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11243             // on BS4 we do this only if not form 
11244             cfg.cls += ' navbar-form';
11245             cfg.tag = 'li';
11246         }
11247         
11248         return cfg;
11249         
11250     },
11251     /**
11252      * return the real input element.
11253      */
11254     inputEl: function ()
11255     {
11256         return this.el.select('input.form-control',true).first();
11257     },
11258     
11259     tooltipEl : function()
11260     {
11261         return this.inputEl();
11262     },
11263     
11264     indicatorEl : function()
11265     {
11266         if (Roo.bootstrap.version == 4) {
11267             return false; // not enabled in v4 yet.
11268         }
11269         
11270         var indicator = this.el.select('i.roo-required-indicator',true).first();
11271         
11272         if(!indicator){
11273             return false;
11274         }
11275         
11276         return indicator;
11277         
11278     },
11279     
11280     setDisabled : function(v)
11281     {
11282         var i  = this.inputEl().dom;
11283         if (!v) {
11284             i.removeAttribute('disabled');
11285             return;
11286             
11287         }
11288         i.setAttribute('disabled','true');
11289     },
11290     initEvents : function()
11291     {
11292           
11293         this.inputEl().on("keydown" , this.fireKey,  this);
11294         this.inputEl().on("focus", this.onFocus,  this);
11295         this.inputEl().on("blur", this.onBlur,  this);
11296         
11297         this.inputEl().relayEvent('keyup', this);
11298         this.inputEl().relayEvent('paste', this);
11299         
11300         this.indicator = this.indicatorEl();
11301         
11302         if(this.indicator){
11303             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11304         }
11305  
11306         // reference to original value for reset
11307         this.originalValue = this.getValue();
11308         //Roo.form.TextField.superclass.initEvents.call(this);
11309         if(this.validationEvent == 'keyup'){
11310             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11311             this.inputEl().on('keyup', this.filterValidation, this);
11312         }
11313         else if(this.validationEvent !== false){
11314             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11315         }
11316         
11317         if(this.selectOnFocus){
11318             this.on("focus", this.preFocus, this);
11319             
11320         }
11321         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11322             this.inputEl().on("keypress", this.filterKeys, this);
11323         } else {
11324             this.inputEl().relayEvent('keypress', this);
11325         }
11326        /* if(this.grow){
11327             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11328             this.el.on("click", this.autoSize,  this);
11329         }
11330         */
11331         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11332             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11333         }
11334         
11335         if (typeof(this.before) == 'object') {
11336             this.before.render(this.el.select('.roo-input-before',true).first());
11337         }
11338         if (typeof(this.after) == 'object') {
11339             this.after.render(this.el.select('.roo-input-after',true).first());
11340         }
11341         
11342         this.inputEl().on('change', this.onChange, this);
11343         
11344     },
11345     filterValidation : function(e){
11346         if(!e.isNavKeyPress()){
11347             this.validationTask.delay(this.validationDelay);
11348         }
11349     },
11350      /**
11351      * Validates the field value
11352      * @return {Boolean} True if the value is valid, else false
11353      */
11354     validate : function(){
11355         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11356         if(this.disabled || this.validateValue(this.getRawValue())){
11357             this.markValid();
11358             return true;
11359         }
11360         
11361         this.markInvalid();
11362         return false;
11363     },
11364     
11365     
11366     /**
11367      * Validates a value according to the field's validation rules and marks the field as invalid
11368      * if the validation fails
11369      * @param {Mixed} value The value to validate
11370      * @return {Boolean} True if the value is valid, else false
11371      */
11372     validateValue : function(value)
11373     {
11374         if(this.getVisibilityEl().hasClass('hidden')){
11375             return true;
11376         }
11377         
11378         if(value.length < 1)  { // if it's blank
11379             if(this.allowBlank){
11380                 return true;
11381             }
11382             return false;
11383         }
11384         
11385         if(value.length < this.minLength){
11386             return false;
11387         }
11388         if(value.length > this.maxLength){
11389             return false;
11390         }
11391         if(this.vtype){
11392             var vt = Roo.form.VTypes;
11393             if(!vt[this.vtype](value, this)){
11394                 return false;
11395             }
11396         }
11397         if(typeof this.validator == "function"){
11398             var msg = this.validator(value);
11399             if(msg !== true){
11400                 return false;
11401             }
11402             if (typeof(msg) == 'string') {
11403                 this.invalidText = msg;
11404             }
11405         }
11406         
11407         if(this.regex && !this.regex.test(value)){
11408             return false;
11409         }
11410         
11411         return true;
11412     },
11413     
11414      // private
11415     fireKey : function(e){
11416         //Roo.log('field ' + e.getKey());
11417         if(e.isNavKeyPress()){
11418             this.fireEvent("specialkey", this, e);
11419         }
11420     },
11421     focus : function (selectText){
11422         if(this.rendered){
11423             this.inputEl().focus();
11424             if(selectText === true){
11425                 this.inputEl().dom.select();
11426             }
11427         }
11428         return this;
11429     } ,
11430     
11431     onFocus : function(){
11432         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11433            // this.el.addClass(this.focusClass);
11434         }
11435         if(!this.hasFocus){
11436             this.hasFocus = true;
11437             this.startValue = this.getValue();
11438             this.fireEvent("focus", this);
11439         }
11440     },
11441     
11442     beforeBlur : Roo.emptyFn,
11443
11444     
11445     // private
11446     onBlur : function(){
11447         this.beforeBlur();
11448         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11449             //this.el.removeClass(this.focusClass);
11450         }
11451         this.hasFocus = false;
11452         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11453             this.validate();
11454         }
11455         var v = this.getValue();
11456         if(String(v) !== String(this.startValue)){
11457             this.fireEvent('change', this, v, this.startValue);
11458         }
11459         this.fireEvent("blur", this);
11460     },
11461     
11462     onChange : function(e)
11463     {
11464         var v = this.getValue();
11465         if(String(v) !== String(this.startValue)){
11466             this.fireEvent('change', this, v, this.startValue);
11467         }
11468         
11469     },
11470     
11471     /**
11472      * Resets the current field value to the originally loaded value and clears any validation messages
11473      */
11474     reset : function(){
11475         this.setValue(this.originalValue);
11476         this.validate();
11477     },
11478      /**
11479      * Returns the name of the field
11480      * @return {Mixed} name The name field
11481      */
11482     getName: function(){
11483         return this.name;
11484     },
11485      /**
11486      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11487      * @return {Mixed} value The field value
11488      */
11489     getValue : function(){
11490         
11491         var v = this.inputEl().getValue();
11492         
11493         return v;
11494     },
11495     /**
11496      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11497      * @return {Mixed} value The field value
11498      */
11499     getRawValue : function(){
11500         var v = this.inputEl().getValue();
11501         
11502         return v;
11503     },
11504     
11505     /**
11506      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11507      * @param {Mixed} value The value to set
11508      */
11509     setRawValue : function(v){
11510         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11511     },
11512     
11513     selectText : function(start, end){
11514         var v = this.getRawValue();
11515         if(v.length > 0){
11516             start = start === undefined ? 0 : start;
11517             end = end === undefined ? v.length : end;
11518             var d = this.inputEl().dom;
11519             if(d.setSelectionRange){
11520                 d.setSelectionRange(start, end);
11521             }else if(d.createTextRange){
11522                 var range = d.createTextRange();
11523                 range.moveStart("character", start);
11524                 range.moveEnd("character", v.length-end);
11525                 range.select();
11526             }
11527         }
11528     },
11529     
11530     /**
11531      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11532      * @param {Mixed} value The value to set
11533      */
11534     setValue : function(v){
11535         this.value = v;
11536         if(this.rendered){
11537             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11538             this.validate();
11539         }
11540     },
11541     
11542     /*
11543     processValue : function(value){
11544         if(this.stripCharsRe){
11545             var newValue = value.replace(this.stripCharsRe, '');
11546             if(newValue !== value){
11547                 this.setRawValue(newValue);
11548                 return newValue;
11549             }
11550         }
11551         return value;
11552     },
11553   */
11554     preFocus : function(){
11555         
11556         if(this.selectOnFocus){
11557             this.inputEl().dom.select();
11558         }
11559     },
11560     filterKeys : function(e){
11561         var k = e.getKey();
11562         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11563             return;
11564         }
11565         var c = e.getCharCode(), cc = String.fromCharCode(c);
11566         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11567             return;
11568         }
11569         if(!this.maskRe.test(cc)){
11570             e.stopEvent();
11571         }
11572     },
11573      /**
11574      * Clear any invalid styles/messages for this field
11575      */
11576     clearInvalid : function(){
11577         
11578         if(!this.el || this.preventMark){ // not rendered
11579             return;
11580         }
11581         
11582         
11583         this.el.removeClass([this.invalidClass, 'is-invalid']);
11584         
11585         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11586             
11587             var feedback = this.el.select('.form-control-feedback', true).first();
11588             
11589             if(feedback){
11590                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11591             }
11592             
11593         }
11594         
11595         if(this.indicator){
11596             this.indicator.removeClass('visible');
11597             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11598         }
11599         
11600         this.fireEvent('valid', this);
11601     },
11602     
11603      /**
11604      * Mark this field as valid
11605      */
11606     markValid : function()
11607     {
11608         if(!this.el  || this.preventMark){ // not rendered...
11609             return;
11610         }
11611         
11612         this.el.removeClass([this.invalidClass, this.validClass]);
11613         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11614
11615         var feedback = this.el.select('.form-control-feedback', true).first();
11616             
11617         if(feedback){
11618             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11619         }
11620         
11621         if(this.indicator){
11622             this.indicator.removeClass('visible');
11623             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11624         }
11625         
11626         if(this.disabled){
11627             return;
11628         }
11629         
11630            
11631         if(this.allowBlank && !this.getRawValue().length){
11632             return;
11633         }
11634         if (Roo.bootstrap.version == 3) {
11635             this.el.addClass(this.validClass);
11636         } else {
11637             this.inputEl().addClass('is-valid');
11638         }
11639
11640         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11641             
11642             var feedback = this.el.select('.form-control-feedback', true).first();
11643             
11644             if(feedback){
11645                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11646                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11647             }
11648             
11649         }
11650         
11651         this.fireEvent('valid', this);
11652     },
11653     
11654      /**
11655      * Mark this field as invalid
11656      * @param {String} msg The validation message
11657      */
11658     markInvalid : function(msg)
11659     {
11660         if(!this.el  || this.preventMark){ // not rendered
11661             return;
11662         }
11663         
11664         this.el.removeClass([this.invalidClass, this.validClass]);
11665         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11666         
11667         var feedback = this.el.select('.form-control-feedback', true).first();
11668             
11669         if(feedback){
11670             this.el.select('.form-control-feedback', true).first().removeClass(
11671                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11672         }
11673
11674         if(this.disabled){
11675             return;
11676         }
11677         
11678         if(this.allowBlank && !this.getRawValue().length){
11679             return;
11680         }
11681         
11682         if(this.indicator){
11683             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11684             this.indicator.addClass('visible');
11685         }
11686         if (Roo.bootstrap.version == 3) {
11687             this.el.addClass(this.invalidClass);
11688         } else {
11689             this.inputEl().addClass('is-invalid');
11690         }
11691         
11692         
11693         
11694         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11695             
11696             var feedback = this.el.select('.form-control-feedback', true).first();
11697             
11698             if(feedback){
11699                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11700                 
11701                 if(this.getValue().length || this.forceFeedback){
11702                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11703                 }
11704                 
11705             }
11706             
11707         }
11708         
11709         this.fireEvent('invalid', this, msg);
11710     },
11711     // private
11712     SafariOnKeyDown : function(event)
11713     {
11714         // this is a workaround for a password hang bug on chrome/ webkit.
11715         if (this.inputEl().dom.type != 'password') {
11716             return;
11717         }
11718         
11719         var isSelectAll = false;
11720         
11721         if(this.inputEl().dom.selectionEnd > 0){
11722             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11723         }
11724         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11725             event.preventDefault();
11726             this.setValue('');
11727             return;
11728         }
11729         
11730         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11731             
11732             event.preventDefault();
11733             // this is very hacky as keydown always get's upper case.
11734             //
11735             var cc = String.fromCharCode(event.getCharCode());
11736             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11737             
11738         }
11739     },
11740     adjustWidth : function(tag, w){
11741         tag = tag.toLowerCase();
11742         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11743             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11744                 if(tag == 'input'){
11745                     return w + 2;
11746                 }
11747                 if(tag == 'textarea'){
11748                     return w-2;
11749                 }
11750             }else if(Roo.isOpera){
11751                 if(tag == 'input'){
11752                     return w + 2;
11753                 }
11754                 if(tag == 'textarea'){
11755                     return w-2;
11756                 }
11757             }
11758         }
11759         return w;
11760     },
11761     
11762     setFieldLabel : function(v)
11763     {
11764         if(!this.rendered){
11765             return;
11766         }
11767         
11768         if(this.indicatorEl()){
11769             var ar = this.el.select('label > span',true);
11770             
11771             if (ar.elements.length) {
11772                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11773                 this.fieldLabel = v;
11774                 return;
11775             }
11776             
11777             var br = this.el.select('label',true);
11778             
11779             if(br.elements.length) {
11780                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11781                 this.fieldLabel = v;
11782                 return;
11783             }
11784             
11785             Roo.log('Cannot Found any of label > span || label in input');
11786             return;
11787         }
11788         
11789         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11790         this.fieldLabel = v;
11791         
11792         
11793     }
11794 });
11795
11796  
11797 /*
11798  * - LGPL
11799  *
11800  * Input
11801  * 
11802  */
11803
11804 /**
11805  * @class Roo.bootstrap.TextArea
11806  * @extends Roo.bootstrap.Input
11807  * Bootstrap TextArea class
11808  * @cfg {Number} cols Specifies the visible width of a text area
11809  * @cfg {Number} rows Specifies the visible number of lines in a text area
11810  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11811  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11812  * @cfg {string} html text
11813  * 
11814  * @constructor
11815  * Create a new TextArea
11816  * @param {Object} config The config object
11817  */
11818
11819 Roo.bootstrap.TextArea = function(config){
11820     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11821    
11822 };
11823
11824 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11825      
11826     cols : false,
11827     rows : 5,
11828     readOnly : false,
11829     warp : 'soft',
11830     resize : false,
11831     value: false,
11832     html: false,
11833     
11834     getAutoCreate : function(){
11835         
11836         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11837         
11838         var id = Roo.id();
11839         
11840         var cfg = {};
11841         
11842         if(this.inputType != 'hidden'){
11843             cfg.cls = 'form-group' //input-group
11844         }
11845         
11846         var input =  {
11847             tag: 'textarea',
11848             id : id,
11849             warp : this.warp,
11850             rows : this.rows,
11851             value : this.value || '',
11852             html: this.html || '',
11853             cls : 'form-control',
11854             placeholder : this.placeholder || '' 
11855             
11856         };
11857         
11858         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11859             input.maxLength = this.maxLength;
11860         }
11861         
11862         if(this.resize){
11863             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11864         }
11865         
11866         if(this.cols){
11867             input.cols = this.cols;
11868         }
11869         
11870         if (this.readOnly) {
11871             input.readonly = true;
11872         }
11873         
11874         if (this.name) {
11875             input.name = this.name;
11876         }
11877         
11878         if (this.size) {
11879             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11880         }
11881         
11882         var settings=this;
11883         ['xs','sm','md','lg'].map(function(size){
11884             if (settings[size]) {
11885                 cfg.cls += ' col-' + size + '-' + settings[size];
11886             }
11887         });
11888         
11889         var inputblock = input;
11890         
11891         if(this.hasFeedback && !this.allowBlank){
11892             
11893             var feedback = {
11894                 tag: 'span',
11895                 cls: 'glyphicon form-control-feedback'
11896             };
11897
11898             inputblock = {
11899                 cls : 'has-feedback',
11900                 cn :  [
11901                     input,
11902                     feedback
11903                 ] 
11904             };  
11905         }
11906         
11907         
11908         if (this.before || this.after) {
11909             
11910             inputblock = {
11911                 cls : 'input-group',
11912                 cn :  [] 
11913             };
11914             if (this.before) {
11915                 inputblock.cn.push({
11916                     tag :'span',
11917                     cls : 'input-group-addon',
11918                     html : this.before
11919                 });
11920             }
11921             
11922             inputblock.cn.push(input);
11923             
11924             if(this.hasFeedback && !this.allowBlank){
11925                 inputblock.cls += ' has-feedback';
11926                 inputblock.cn.push(feedback);
11927             }
11928             
11929             if (this.after) {
11930                 inputblock.cn.push({
11931                     tag :'span',
11932                     cls : 'input-group-addon',
11933                     html : this.after
11934                 });
11935             }
11936             
11937         }
11938         
11939         if (align ==='left' && this.fieldLabel.length) {
11940             cfg.cn = [
11941                 {
11942                     tag: 'label',
11943                     'for' :  id,
11944                     cls : 'control-label',
11945                     html : this.fieldLabel
11946                 },
11947                 {
11948                     cls : "",
11949                     cn: [
11950                         inputblock
11951                     ]
11952                 }
11953
11954             ];
11955             
11956             if(this.labelWidth > 12){
11957                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11958             }
11959
11960             if(this.labelWidth < 13 && this.labelmd == 0){
11961                 this.labelmd = this.labelWidth;
11962             }
11963
11964             if(this.labellg > 0){
11965                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11966                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11967             }
11968
11969             if(this.labelmd > 0){
11970                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11971                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11972             }
11973
11974             if(this.labelsm > 0){
11975                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11976                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11977             }
11978
11979             if(this.labelxs > 0){
11980                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11981                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11982             }
11983             
11984         } else if ( this.fieldLabel.length) {
11985             cfg.cn = [
11986
11987                {
11988                    tag: 'label',
11989                    //cls : 'input-group-addon',
11990                    html : this.fieldLabel
11991
11992                },
11993
11994                inputblock
11995
11996            ];
11997
11998         } else {
11999
12000             cfg.cn = [
12001
12002                 inputblock
12003
12004             ];
12005                 
12006         }
12007         
12008         if (this.disabled) {
12009             input.disabled=true;
12010         }
12011         
12012         return cfg;
12013         
12014     },
12015     /**
12016      * return the real textarea element.
12017      */
12018     inputEl: function ()
12019     {
12020         return this.el.select('textarea.form-control',true).first();
12021     },
12022     
12023     /**
12024      * Clear any invalid styles/messages for this field
12025      */
12026     clearInvalid : function()
12027     {
12028         
12029         if(!this.el || this.preventMark){ // not rendered
12030             return;
12031         }
12032         
12033         var label = this.el.select('label', true).first();
12034         var icon = this.el.select('i.fa-star', true).first();
12035         
12036         if(label && icon){
12037             icon.remove();
12038         }
12039         this.el.removeClass( this.validClass);
12040         this.inputEl().removeClass('is-invalid');
12041          
12042         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12043             
12044             var feedback = this.el.select('.form-control-feedback', true).first();
12045             
12046             if(feedback){
12047                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12048             }
12049             
12050         }
12051         
12052         this.fireEvent('valid', this);
12053     },
12054     
12055      /**
12056      * Mark this field as valid
12057      */
12058     markValid : function()
12059     {
12060         if(!this.el  || this.preventMark){ // not rendered
12061             return;
12062         }
12063         
12064         this.el.removeClass([this.invalidClass, this.validClass]);
12065         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12066         
12067         var feedback = this.el.select('.form-control-feedback', true).first();
12068             
12069         if(feedback){
12070             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12071         }
12072
12073         if(this.disabled || this.allowBlank){
12074             return;
12075         }
12076         
12077         var label = this.el.select('label', true).first();
12078         var icon = this.el.select('i.fa-star', true).first();
12079         
12080         if(label && icon){
12081             icon.remove();
12082         }
12083         if (Roo.bootstrap.version == 3) {
12084             this.el.addClass(this.validClass);
12085         } else {
12086             this.inputEl().addClass('is-valid');
12087         }
12088         
12089         
12090         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12091             
12092             var feedback = this.el.select('.form-control-feedback', true).first();
12093             
12094             if(feedback){
12095                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12096                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12097             }
12098             
12099         }
12100         
12101         this.fireEvent('valid', this);
12102     },
12103     
12104      /**
12105      * Mark this field as invalid
12106      * @param {String} msg The validation message
12107      */
12108     markInvalid : function(msg)
12109     {
12110         if(!this.el  || this.preventMark){ // not rendered
12111             return;
12112         }
12113         
12114         this.el.removeClass([this.invalidClass, this.validClass]);
12115         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12116         
12117         var feedback = this.el.select('.form-control-feedback', true).first();
12118             
12119         if(feedback){
12120             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12121         }
12122
12123         if(this.disabled || this.allowBlank){
12124             return;
12125         }
12126         
12127         var label = this.el.select('label', true).first();
12128         var icon = this.el.select('i.fa-star', true).first();
12129         
12130         if(!this.getValue().length && label && !icon){
12131             this.el.createChild({
12132                 tag : 'i',
12133                 cls : 'text-danger fa fa-lg fa-star',
12134                 tooltip : 'This field is required',
12135                 style : 'margin-right:5px;'
12136             }, label, true);
12137         }
12138         
12139         if (Roo.bootstrap.version == 3) {
12140             this.el.addClass(this.invalidClass);
12141         } else {
12142             this.inputEl().addClass('is-invalid');
12143         }
12144         
12145         // fixme ... this may be depricated need to test..
12146         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12147             
12148             var feedback = this.el.select('.form-control-feedback', true).first();
12149             
12150             if(feedback){
12151                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12152                 
12153                 if(this.getValue().length || this.forceFeedback){
12154                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12155                 }
12156                 
12157             }
12158             
12159         }
12160         
12161         this.fireEvent('invalid', this, msg);
12162     }
12163 });
12164
12165  
12166 /*
12167  * - LGPL
12168  *
12169  * trigger field - base class for combo..
12170  * 
12171  */
12172  
12173 /**
12174  * @class Roo.bootstrap.TriggerField
12175  * @extends Roo.bootstrap.Input
12176  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12177  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12178  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12179  * for which you can provide a custom implementation.  For example:
12180  * <pre><code>
12181 var trigger = new Roo.bootstrap.TriggerField();
12182 trigger.onTriggerClick = myTriggerFn;
12183 trigger.applyTo('my-field');
12184 </code></pre>
12185  *
12186  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12187  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12188  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12189  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12190  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12191
12192  * @constructor
12193  * Create a new TriggerField.
12194  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12195  * to the base TextField)
12196  */
12197 Roo.bootstrap.TriggerField = function(config){
12198     this.mimicing = false;
12199     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12200 };
12201
12202 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12203     /**
12204      * @cfg {String} triggerClass A CSS class to apply to the trigger
12205      */
12206      /**
12207      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12208      */
12209     hideTrigger:false,
12210
12211     /**
12212      * @cfg {Boolean} removable (true|false) special filter default false
12213      */
12214     removable : false,
12215     
12216     /** @cfg {Boolean} grow @hide */
12217     /** @cfg {Number} growMin @hide */
12218     /** @cfg {Number} growMax @hide */
12219
12220     /**
12221      * @hide 
12222      * @method
12223      */
12224     autoSize: Roo.emptyFn,
12225     // private
12226     monitorTab : true,
12227     // private
12228     deferHeight : true,
12229
12230     
12231     actionMode : 'wrap',
12232     
12233     caret : false,
12234     
12235     
12236     getAutoCreate : function(){
12237        
12238         var align = this.labelAlign || this.parentLabelAlign();
12239         
12240         var id = Roo.id();
12241         
12242         var cfg = {
12243             cls: 'form-group' //input-group
12244         };
12245         
12246         
12247         var input =  {
12248             tag: 'input',
12249             id : id,
12250             type : this.inputType,
12251             cls : 'form-control',
12252             autocomplete: 'new-password',
12253             placeholder : this.placeholder || '' 
12254             
12255         };
12256         if (this.name) {
12257             input.name = this.name;
12258         }
12259         if (this.size) {
12260             input.cls += ' input-' + this.size;
12261         }
12262         
12263         if (this.disabled) {
12264             input.disabled=true;
12265         }
12266         
12267         var inputblock = input;
12268         
12269         if(this.hasFeedback && !this.allowBlank){
12270             
12271             var feedback = {
12272                 tag: 'span',
12273                 cls: 'glyphicon form-control-feedback'
12274             };
12275             
12276             if(this.removable && !this.editable  ){
12277                 inputblock = {
12278                     cls : 'has-feedback',
12279                     cn :  [
12280                         inputblock,
12281                         {
12282                             tag: 'button',
12283                             html : 'x',
12284                             cls : 'roo-combo-removable-btn close'
12285                         },
12286                         feedback
12287                     ] 
12288                 };
12289             } else {
12290                 inputblock = {
12291                     cls : 'has-feedback',
12292                     cn :  [
12293                         inputblock,
12294                         feedback
12295                     ] 
12296                 };
12297             }
12298
12299         } else {
12300             if(this.removable && !this.editable ){
12301                 inputblock = {
12302                     cls : 'roo-removable',
12303                     cn :  [
12304                         inputblock,
12305                         {
12306                             tag: 'button',
12307                             html : 'x',
12308                             cls : 'roo-combo-removable-btn close'
12309                         }
12310                     ] 
12311                 };
12312             }
12313         }
12314         
12315         if (this.before || this.after) {
12316             
12317             inputblock = {
12318                 cls : 'input-group',
12319                 cn :  [] 
12320             };
12321             if (this.before) {
12322                 inputblock.cn.push({
12323                     tag :'span',
12324                     cls : 'input-group-addon input-group-prepend input-group-text',
12325                     html : this.before
12326                 });
12327             }
12328             
12329             inputblock.cn.push(input);
12330             
12331             if(this.hasFeedback && !this.allowBlank){
12332                 inputblock.cls += ' has-feedback';
12333                 inputblock.cn.push(feedback);
12334             }
12335             
12336             if (this.after) {
12337                 inputblock.cn.push({
12338                     tag :'span',
12339                     cls : 'input-group-addon input-group-append input-group-text',
12340                     html : this.after
12341                 });
12342             }
12343             
12344         };
12345         
12346       
12347         
12348         var ibwrap = inputblock;
12349         
12350         if(this.multiple){
12351             ibwrap = {
12352                 tag: 'ul',
12353                 cls: 'roo-select2-choices',
12354                 cn:[
12355                     {
12356                         tag: 'li',
12357                         cls: 'roo-select2-search-field',
12358                         cn: [
12359
12360                             inputblock
12361                         ]
12362                     }
12363                 ]
12364             };
12365                 
12366         }
12367         
12368         var combobox = {
12369             cls: 'roo-select2-container input-group',
12370             cn: [
12371                  {
12372                     tag: 'input',
12373                     type : 'hidden',
12374                     cls: 'form-hidden-field'
12375                 },
12376                 ibwrap
12377             ]
12378         };
12379         
12380         if(!this.multiple && this.showToggleBtn){
12381             
12382             var caret = {
12383                         tag: 'span',
12384                         cls: 'caret'
12385              };
12386             if (this.caret != false) {
12387                 caret = {
12388                      tag: 'i',
12389                      cls: 'fa fa-' + this.caret
12390                 };
12391                 
12392             }
12393             
12394             combobox.cn.push({
12395                 tag :'span',
12396                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12397                 cn : [
12398                     Roo.bootstrap.version == 3 ? caret : '',
12399                     {
12400                         tag: 'span',
12401                         cls: 'combobox-clear',
12402                         cn  : [
12403                             {
12404                                 tag : 'i',
12405                                 cls: 'icon-remove'
12406                             }
12407                         ]
12408                     }
12409                 ]
12410
12411             })
12412         }
12413         
12414         if(this.multiple){
12415             combobox.cls += ' roo-select2-container-multi';
12416         }
12417          var indicator = {
12418             tag : 'i',
12419             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12420             tooltip : 'This field is required'
12421         };
12422         if (Roo.bootstrap.version == 4) {
12423             indicator = {
12424                 tag : 'i',
12425                 style : 'display:none'
12426             };
12427         }
12428         
12429         
12430         if (align ==='left' && this.fieldLabel.length) {
12431             
12432             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12433
12434             cfg.cn = [
12435                 indicator,
12436                 {
12437                     tag: 'label',
12438                     'for' :  id,
12439                     cls : 'control-label',
12440                     html : this.fieldLabel
12441
12442                 },
12443                 {
12444                     cls : "", 
12445                     cn: [
12446                         combobox
12447                     ]
12448                 }
12449
12450             ];
12451             
12452             var labelCfg = cfg.cn[1];
12453             var contentCfg = cfg.cn[2];
12454             
12455             if(this.indicatorpos == 'right'){
12456                 cfg.cn = [
12457                     {
12458                         tag: 'label',
12459                         'for' :  id,
12460                         cls : 'control-label',
12461                         cn : [
12462                             {
12463                                 tag : 'span',
12464                                 html : this.fieldLabel
12465                             },
12466                             indicator
12467                         ]
12468                     },
12469                     {
12470                         cls : "", 
12471                         cn: [
12472                             combobox
12473                         ]
12474                     }
12475
12476                 ];
12477                 
12478                 labelCfg = cfg.cn[0];
12479                 contentCfg = cfg.cn[1];
12480             }
12481             
12482             if(this.labelWidth > 12){
12483                 labelCfg.style = "width: " + this.labelWidth + 'px';
12484             }
12485             
12486             if(this.labelWidth < 13 && this.labelmd == 0){
12487                 this.labelmd = this.labelWidth;
12488             }
12489             
12490             if(this.labellg > 0){
12491                 labelCfg.cls += ' col-lg-' + this.labellg;
12492                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12493             }
12494             
12495             if(this.labelmd > 0){
12496                 labelCfg.cls += ' col-md-' + this.labelmd;
12497                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12498             }
12499             
12500             if(this.labelsm > 0){
12501                 labelCfg.cls += ' col-sm-' + this.labelsm;
12502                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12503             }
12504             
12505             if(this.labelxs > 0){
12506                 labelCfg.cls += ' col-xs-' + this.labelxs;
12507                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12508             }
12509             
12510         } else if ( this.fieldLabel.length) {
12511 //                Roo.log(" label");
12512             cfg.cn = [
12513                 indicator,
12514                {
12515                    tag: 'label',
12516                    //cls : 'input-group-addon',
12517                    html : this.fieldLabel
12518
12519                },
12520
12521                combobox
12522
12523             ];
12524             
12525             if(this.indicatorpos == 'right'){
12526                 
12527                 cfg.cn = [
12528                     {
12529                        tag: 'label',
12530                        cn : [
12531                            {
12532                                tag : 'span',
12533                                html : this.fieldLabel
12534                            },
12535                            indicator
12536                        ]
12537
12538                     },
12539                     combobox
12540
12541                 ];
12542
12543             }
12544
12545         } else {
12546             
12547 //                Roo.log(" no label && no align");
12548                 cfg = combobox
12549                      
12550                 
12551         }
12552         
12553         var settings=this;
12554         ['xs','sm','md','lg'].map(function(size){
12555             if (settings[size]) {
12556                 cfg.cls += ' col-' + size + '-' + settings[size];
12557             }
12558         });
12559         
12560         return cfg;
12561         
12562     },
12563     
12564     
12565     
12566     // private
12567     onResize : function(w, h){
12568 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12569 //        if(typeof w == 'number'){
12570 //            var x = w - this.trigger.getWidth();
12571 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12572 //            this.trigger.setStyle('left', x+'px');
12573 //        }
12574     },
12575
12576     // private
12577     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12578
12579     // private
12580     getResizeEl : function(){
12581         return this.inputEl();
12582     },
12583
12584     // private
12585     getPositionEl : function(){
12586         return this.inputEl();
12587     },
12588
12589     // private
12590     alignErrorIcon : function(){
12591         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12592     },
12593
12594     // private
12595     initEvents : function(){
12596         
12597         this.createList();
12598         
12599         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12600         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12601         if(!this.multiple && this.showToggleBtn){
12602             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12603             if(this.hideTrigger){
12604                 this.trigger.setDisplayed(false);
12605             }
12606             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12607         }
12608         
12609         if(this.multiple){
12610             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12611         }
12612         
12613         if(this.removable && !this.editable && !this.tickable){
12614             var close = this.closeTriggerEl();
12615             
12616             if(close){
12617                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12618                 close.on('click', this.removeBtnClick, this, close);
12619             }
12620         }
12621         
12622         //this.trigger.addClassOnOver('x-form-trigger-over');
12623         //this.trigger.addClassOnClick('x-form-trigger-click');
12624         
12625         //if(!this.width){
12626         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12627         //}
12628     },
12629     
12630     closeTriggerEl : function()
12631     {
12632         var close = this.el.select('.roo-combo-removable-btn', true).first();
12633         return close ? close : false;
12634     },
12635     
12636     removeBtnClick : function(e, h, el)
12637     {
12638         e.preventDefault();
12639         
12640         if(this.fireEvent("remove", this) !== false){
12641             this.reset();
12642             this.fireEvent("afterremove", this)
12643         }
12644     },
12645     
12646     createList : function()
12647     {
12648         this.list = Roo.get(document.body).createChild({
12649             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12650             cls: 'typeahead typeahead-long dropdown-menu shadow',
12651             style: 'display:none'
12652         });
12653         
12654         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12655         
12656     },
12657
12658     // private
12659     initTrigger : function(){
12660        
12661     },
12662
12663     // private
12664     onDestroy : function(){
12665         if(this.trigger){
12666             this.trigger.removeAllListeners();
12667           //  this.trigger.remove();
12668         }
12669         //if(this.wrap){
12670         //    this.wrap.remove();
12671         //}
12672         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12673     },
12674
12675     // private
12676     onFocus : function(){
12677         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12678         /*
12679         if(!this.mimicing){
12680             this.wrap.addClass('x-trigger-wrap-focus');
12681             this.mimicing = true;
12682             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12683             if(this.monitorTab){
12684                 this.el.on("keydown", this.checkTab, this);
12685             }
12686         }
12687         */
12688     },
12689
12690     // private
12691     checkTab : function(e){
12692         if(e.getKey() == e.TAB){
12693             this.triggerBlur();
12694         }
12695     },
12696
12697     // private
12698     onBlur : function(){
12699         // do nothing
12700     },
12701
12702     // private
12703     mimicBlur : function(e, t){
12704         /*
12705         if(!this.wrap.contains(t) && this.validateBlur()){
12706             this.triggerBlur();
12707         }
12708         */
12709     },
12710
12711     // private
12712     triggerBlur : function(){
12713         this.mimicing = false;
12714         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12715         if(this.monitorTab){
12716             this.el.un("keydown", this.checkTab, this);
12717         }
12718         //this.wrap.removeClass('x-trigger-wrap-focus');
12719         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12720     },
12721
12722     // private
12723     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12724     validateBlur : function(e, t){
12725         return true;
12726     },
12727
12728     // private
12729     onDisable : function(){
12730         this.inputEl().dom.disabled = true;
12731         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12732         //if(this.wrap){
12733         //    this.wrap.addClass('x-item-disabled');
12734         //}
12735     },
12736
12737     // private
12738     onEnable : function(){
12739         this.inputEl().dom.disabled = false;
12740         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12741         //if(this.wrap){
12742         //    this.el.removeClass('x-item-disabled');
12743         //}
12744     },
12745
12746     // private
12747     onShow : function(){
12748         var ae = this.getActionEl();
12749         
12750         if(ae){
12751             ae.dom.style.display = '';
12752             ae.dom.style.visibility = 'visible';
12753         }
12754     },
12755
12756     // private
12757     
12758     onHide : function(){
12759         var ae = this.getActionEl();
12760         ae.dom.style.display = 'none';
12761     },
12762
12763     /**
12764      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12765      * by an implementing function.
12766      * @method
12767      * @param {EventObject} e
12768      */
12769     onTriggerClick : Roo.emptyFn
12770 });
12771  
12772 /*
12773 * Licence: LGPL
12774 */
12775
12776 /**
12777  * @class Roo.bootstrap.CardUploader
12778  * @extends Roo.bootstrap.Button
12779  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12780  * @cfg {Number} errorTimeout default 3000
12781  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12782  * @cfg {Array}  html The button text.
12783
12784  *
12785  * @constructor
12786  * Create a new CardUploader
12787  * @param {Object} config The config object
12788  */
12789
12790 Roo.bootstrap.CardUploader = function(config){
12791     
12792  
12793     
12794     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12795     
12796     
12797     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12798         return r.data.id
12799      });
12800     
12801      this.addEvents({
12802          // raw events
12803         /**
12804          * @event preview
12805          * When a image is clicked on - and needs to display a slideshow or similar..
12806          * @param {Roo.bootstrap.Card} this
12807          * @param {Object} The image information data 
12808          *
12809          */
12810         'preview' : true,
12811          /**
12812          * @event download
12813          * When a the download link is clicked
12814          * @param {Roo.bootstrap.Card} this
12815          * @param {Object} The image information data  contains 
12816          */
12817         'download' : true
12818         
12819     });
12820 };
12821  
12822 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12823     
12824      
12825     errorTimeout : 3000,
12826      
12827     images : false,
12828    
12829     fileCollection : false,
12830     allowBlank : true,
12831     
12832     getAutoCreate : function()
12833     {
12834         
12835         var cfg =  {
12836             cls :'form-group' ,
12837             cn : [
12838                
12839                 {
12840                     tag: 'label',
12841                    //cls : 'input-group-addon',
12842                     html : this.fieldLabel
12843
12844                 },
12845
12846                 {
12847                     tag: 'input',
12848                     type : 'hidden',
12849                     name : this.name,
12850                     value : this.value,
12851                     cls : 'd-none  form-control'
12852                 },
12853                 
12854                 {
12855                     tag: 'input',
12856                     multiple : 'multiple',
12857                     type : 'file',
12858                     cls : 'd-none  roo-card-upload-selector'
12859                 },
12860                 
12861                 {
12862                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12863                 },
12864                 {
12865                     cls : 'card-columns roo-card-uploader-container'
12866                 }
12867
12868             ]
12869         };
12870            
12871          
12872         return cfg;
12873     },
12874     
12875     getChildContainer : function() /// what children are added to.
12876     {
12877         return this.containerEl;
12878     },
12879    
12880     getButtonContainer : function() /// what children are added to.
12881     {
12882         return this.el.select(".roo-card-uploader-button-container").first();
12883     },
12884    
12885     initEvents : function()
12886     {
12887         
12888         Roo.bootstrap.Input.prototype.initEvents.call(this);
12889         
12890         var t = this;
12891         this.addxtype({
12892             xns: Roo.bootstrap,
12893
12894             xtype : 'Button',
12895             container_method : 'getButtonContainer' ,            
12896             html :  this.html, // fix changable?
12897             cls : 'w-100 ',
12898             listeners : {
12899                 'click' : function(btn, e) {
12900                     t.onClick(e);
12901                 }
12902             }
12903         });
12904         
12905         
12906         
12907         
12908         this.urlAPI = (window.createObjectURL && window) || 
12909                                 (window.URL && URL.revokeObjectURL && URL) || 
12910                                 (window.webkitURL && webkitURL);
12911                         
12912          
12913          
12914          
12915         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12916         
12917         this.selectorEl.on('change', this.onFileSelected, this);
12918         if (this.images) {
12919             var t = this;
12920             this.images.forEach(function(img) {
12921                 t.addCard(img)
12922             });
12923             this.images = false;
12924         }
12925         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12926          
12927        
12928     },
12929     
12930    
12931     onClick : function(e)
12932     {
12933         e.preventDefault();
12934          
12935         this.selectorEl.dom.click();
12936          
12937     },
12938     
12939     onFileSelected : function(e)
12940     {
12941         e.preventDefault();
12942         
12943         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12944             return;
12945         }
12946         
12947         Roo.each(this.selectorEl.dom.files, function(file){    
12948             this.addFile(file);
12949         }, this);
12950          
12951     },
12952     
12953       
12954     
12955       
12956     
12957     addFile : function(file)
12958     {
12959            
12960         if(typeof(file) === 'string'){
12961             throw "Add file by name?"; // should not happen
12962             return;
12963         }
12964         
12965         if(!file || !this.urlAPI){
12966             return;
12967         }
12968         
12969         // file;
12970         // file.type;
12971         
12972         var _this = this;
12973         
12974         
12975         var url = _this.urlAPI.createObjectURL( file);
12976            
12977         this.addCard({
12978             id : Roo.bootstrap.CardUploader.ID--,
12979             is_uploaded : false,
12980             src : url,
12981             srcfile : file,
12982             title : file.name,
12983             mimetype : file.type,
12984             preview : false,
12985             is_deleted : 0
12986         });
12987         
12988     },
12989     
12990     /**
12991      * addCard - add an Attachment to the uploader
12992      * @param data - the data about the image to upload
12993      *
12994      * {
12995           id : 123
12996           title : "Title of file",
12997           is_uploaded : false,
12998           src : "http://.....",
12999           srcfile : { the File upload object },
13000           mimetype : file.type,
13001           preview : false,
13002           is_deleted : 0
13003           .. any other data...
13004         }
13005      *
13006      * 
13007     */
13008     
13009     addCard : function (data)
13010     {
13011         // hidden input element?
13012         // if the file is not an image...
13013         //then we need to use something other that and header_image
13014         var t = this;
13015         //   remove.....
13016         var footer = [
13017             {
13018                 xns : Roo.bootstrap,
13019                 xtype : 'CardFooter',
13020                  items: [
13021                     {
13022                         xns : Roo.bootstrap,
13023                         xtype : 'Element',
13024                         cls : 'd-flex',
13025                         items : [
13026                             
13027                             {
13028                                 xns : Roo.bootstrap,
13029                                 xtype : 'Button',
13030                                 html : String.format("<small>{0}</small>", data.title),
13031                                 cls : 'col-10 text-left',
13032                                 size: 'sm',
13033                                 weight: 'link',
13034                                 fa : 'download',
13035                                 listeners : {
13036                                     click : function() {
13037                                      
13038                                         t.fireEvent( "download", t, data );
13039                                     }
13040                                 }
13041                             },
13042                           
13043                             {
13044                                 xns : Roo.bootstrap,
13045                                 xtype : 'Button',
13046                                 style: 'max-height: 28px; ',
13047                                 size : 'sm',
13048                                 weight: 'danger',
13049                                 cls : 'col-2',
13050                                 fa : 'times',
13051                                 listeners : {
13052                                     click : function() {
13053                                         t.removeCard(data.id)
13054                                     }
13055                                 }
13056                             }
13057                         ]
13058                     }
13059                     
13060                 ] 
13061             }
13062             
13063         ];
13064         
13065         var cn = this.addxtype(
13066             {
13067                  
13068                 xns : Roo.bootstrap,
13069                 xtype : 'Card',
13070                 closeable : true,
13071                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13072                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13073                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13074                 data : data,
13075                 html : false,
13076                  
13077                 items : footer,
13078                 initEvents : function() {
13079                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13080                     var card = this;
13081                     this.imgEl = this.el.select('.card-img-top').first();
13082                     if (this.imgEl) {
13083                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13084                         this.imgEl.set({ 'pointer' : 'cursor' });
13085                                   
13086                     }
13087                     this.getCardFooter().addClass('p-1');
13088                     
13089                   
13090                 }
13091                 
13092             }
13093         );
13094         // dont' really need ot update items.
13095         // this.items.push(cn);
13096         this.fileCollection.add(cn);
13097         
13098         if (!data.srcfile) {
13099             this.updateInput();
13100             return;
13101         }
13102             
13103         var _t = this;
13104         var reader = new FileReader();
13105         reader.addEventListener("load", function() {  
13106             data.srcdata =  reader.result;
13107             _t.updateInput();
13108         });
13109         reader.readAsDataURL(data.srcfile);
13110         
13111         
13112         
13113     },
13114     removeCard : function(id)
13115     {
13116         
13117         var card  = this.fileCollection.get(id);
13118         card.data.is_deleted = 1;
13119         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13120         //this.fileCollection.remove(card);
13121         //this.items = this.items.filter(function(e) { return e != card });
13122         // dont' really need ot update items.
13123         card.el.dom.parentNode.removeChild(card.el.dom);
13124         this.updateInput();
13125
13126         
13127     },
13128     reset: function()
13129     {
13130         this.fileCollection.each(function(card) {
13131             if (card.el.dom && card.el.dom.parentNode) {
13132                 card.el.dom.parentNode.removeChild(card.el.dom);
13133             }
13134         });
13135         this.fileCollection.clear();
13136         this.updateInput();
13137     },
13138     
13139     updateInput : function()
13140     {
13141          var data = [];
13142         this.fileCollection.each(function(e) {
13143             data.push(e.data);
13144             
13145         });
13146         this.inputEl().dom.value = JSON.stringify(data);
13147         
13148         
13149         
13150     }
13151     
13152     
13153 });
13154
13155
13156 Roo.bootstrap.CardUploader.ID = -1;/*
13157  * Based on:
13158  * Ext JS Library 1.1.1
13159  * Copyright(c) 2006-2007, Ext JS, LLC.
13160  *
13161  * Originally Released Under LGPL - original licence link has changed is not relivant.
13162  *
13163  * Fork - LGPL
13164  * <script type="text/javascript">
13165  */
13166
13167
13168 /**
13169  * @class Roo.data.SortTypes
13170  * @singleton
13171  * Defines the default sorting (casting?) comparison functions used when sorting data.
13172  */
13173 Roo.data.SortTypes = {
13174     /**
13175      * Default sort that does nothing
13176      * @param {Mixed} s The value being converted
13177      * @return {Mixed} The comparison value
13178      */
13179     none : function(s){
13180         return s;
13181     },
13182     
13183     /**
13184      * The regular expression used to strip tags
13185      * @type {RegExp}
13186      * @property
13187      */
13188     stripTagsRE : /<\/?[^>]+>/gi,
13189     
13190     /**
13191      * Strips all HTML tags to sort on text only
13192      * @param {Mixed} s The value being converted
13193      * @return {String} The comparison value
13194      */
13195     asText : function(s){
13196         return String(s).replace(this.stripTagsRE, "");
13197     },
13198     
13199     /**
13200      * Strips all HTML tags to sort on text only - Case insensitive
13201      * @param {Mixed} s The value being converted
13202      * @return {String} The comparison value
13203      */
13204     asUCText : function(s){
13205         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13206     },
13207     
13208     /**
13209      * Case insensitive string
13210      * @param {Mixed} s The value being converted
13211      * @return {String} The comparison value
13212      */
13213     asUCString : function(s) {
13214         return String(s).toUpperCase();
13215     },
13216     
13217     /**
13218      * Date sorting
13219      * @param {Mixed} s The value being converted
13220      * @return {Number} The comparison value
13221      */
13222     asDate : function(s) {
13223         if(!s){
13224             return 0;
13225         }
13226         if(s instanceof Date){
13227             return s.getTime();
13228         }
13229         return Date.parse(String(s));
13230     },
13231     
13232     /**
13233      * Float sorting
13234      * @param {Mixed} s The value being converted
13235      * @return {Float} The comparison value
13236      */
13237     asFloat : function(s) {
13238         var val = parseFloat(String(s).replace(/,/g, ""));
13239         if(isNaN(val)) {
13240             val = 0;
13241         }
13242         return val;
13243     },
13244     
13245     /**
13246      * Integer sorting
13247      * @param {Mixed} s The value being converted
13248      * @return {Number} The comparison value
13249      */
13250     asInt : function(s) {
13251         var val = parseInt(String(s).replace(/,/g, ""));
13252         if(isNaN(val)) {
13253             val = 0;
13254         }
13255         return val;
13256     }
13257 };/*
13258  * Based on:
13259  * Ext JS Library 1.1.1
13260  * Copyright(c) 2006-2007, Ext JS, LLC.
13261  *
13262  * Originally Released Under LGPL - original licence link has changed is not relivant.
13263  *
13264  * Fork - LGPL
13265  * <script type="text/javascript">
13266  */
13267
13268 /**
13269 * @class Roo.data.Record
13270  * Instances of this class encapsulate both record <em>definition</em> information, and record
13271  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13272  * to access Records cached in an {@link Roo.data.Store} object.<br>
13273  * <p>
13274  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13275  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13276  * objects.<br>
13277  * <p>
13278  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13279  * @constructor
13280  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13281  * {@link #create}. The parameters are the same.
13282  * @param {Array} data An associative Array of data values keyed by the field name.
13283  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13284  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13285  * not specified an integer id is generated.
13286  */
13287 Roo.data.Record = function(data, id){
13288     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13289     this.data = data;
13290 };
13291
13292 /**
13293  * Generate a constructor for a specific record layout.
13294  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13295  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13296  * Each field definition object may contain the following properties: <ul>
13297  * <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,
13298  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13299  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13300  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13301  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13302  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13303  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13304  * this may be omitted.</p></li>
13305  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13306  * <ul><li>auto (Default, implies no conversion)</li>
13307  * <li>string</li>
13308  * <li>int</li>
13309  * <li>float</li>
13310  * <li>boolean</li>
13311  * <li>date</li></ul></p></li>
13312  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13313  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13314  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13315  * by the Reader into an object that will be stored in the Record. It is passed the
13316  * following parameters:<ul>
13317  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13318  * </ul></p></li>
13319  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13320  * </ul>
13321  * <br>usage:<br><pre><code>
13322 var TopicRecord = Roo.data.Record.create(
13323     {name: 'title', mapping: 'topic_title'},
13324     {name: 'author', mapping: 'username'},
13325     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13326     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13327     {name: 'lastPoster', mapping: 'user2'},
13328     {name: 'excerpt', mapping: 'post_text'}
13329 );
13330
13331 var myNewRecord = new TopicRecord({
13332     title: 'Do my job please',
13333     author: 'noobie',
13334     totalPosts: 1,
13335     lastPost: new Date(),
13336     lastPoster: 'Animal',
13337     excerpt: 'No way dude!'
13338 });
13339 myStore.add(myNewRecord);
13340 </code></pre>
13341  * @method create
13342  * @static
13343  */
13344 Roo.data.Record.create = function(o){
13345     var f = function(){
13346         f.superclass.constructor.apply(this, arguments);
13347     };
13348     Roo.extend(f, Roo.data.Record);
13349     var p = f.prototype;
13350     p.fields = new Roo.util.MixedCollection(false, function(field){
13351         return field.name;
13352     });
13353     for(var i = 0, len = o.length; i < len; i++){
13354         p.fields.add(new Roo.data.Field(o[i]));
13355     }
13356     f.getField = function(name){
13357         return p.fields.get(name);  
13358     };
13359     return f;
13360 };
13361
13362 Roo.data.Record.AUTO_ID = 1000;
13363 Roo.data.Record.EDIT = 'edit';
13364 Roo.data.Record.REJECT = 'reject';
13365 Roo.data.Record.COMMIT = 'commit';
13366
13367 Roo.data.Record.prototype = {
13368     /**
13369      * Readonly flag - true if this record has been modified.
13370      * @type Boolean
13371      */
13372     dirty : false,
13373     editing : false,
13374     error: null,
13375     modified: null,
13376
13377     // private
13378     join : function(store){
13379         this.store = store;
13380     },
13381
13382     /**
13383      * Set the named field to the specified value.
13384      * @param {String} name The name of the field to set.
13385      * @param {Object} value The value to set the field to.
13386      */
13387     set : function(name, value){
13388         if(this.data[name] == value){
13389             return;
13390         }
13391         this.dirty = true;
13392         if(!this.modified){
13393             this.modified = {};
13394         }
13395         if(typeof this.modified[name] == 'undefined'){
13396             this.modified[name] = this.data[name];
13397         }
13398         this.data[name] = value;
13399         if(!this.editing && this.store){
13400             this.store.afterEdit(this);
13401         }       
13402     },
13403
13404     /**
13405      * Get the value of the named field.
13406      * @param {String} name The name of the field to get the value of.
13407      * @return {Object} The value of the field.
13408      */
13409     get : function(name){
13410         return this.data[name]; 
13411     },
13412
13413     // private
13414     beginEdit : function(){
13415         this.editing = true;
13416         this.modified = {}; 
13417     },
13418
13419     // private
13420     cancelEdit : function(){
13421         this.editing = false;
13422         delete this.modified;
13423     },
13424
13425     // private
13426     endEdit : function(){
13427         this.editing = false;
13428         if(this.dirty && this.store){
13429             this.store.afterEdit(this);
13430         }
13431     },
13432
13433     /**
13434      * Usually called by the {@link Roo.data.Store} which owns the Record.
13435      * Rejects all changes made to the Record since either creation, or the last commit operation.
13436      * Modified fields are reverted to their original values.
13437      * <p>
13438      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13439      * of reject operations.
13440      */
13441     reject : function(){
13442         var m = this.modified;
13443         for(var n in m){
13444             if(typeof m[n] != "function"){
13445                 this.data[n] = m[n];
13446             }
13447         }
13448         this.dirty = false;
13449         delete this.modified;
13450         this.editing = false;
13451         if(this.store){
13452             this.store.afterReject(this);
13453         }
13454     },
13455
13456     /**
13457      * Usually called by the {@link Roo.data.Store} which owns the Record.
13458      * Commits all changes made to the Record since either creation, or the last commit operation.
13459      * <p>
13460      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13461      * of commit operations.
13462      */
13463     commit : function(){
13464         this.dirty = false;
13465         delete this.modified;
13466         this.editing = false;
13467         if(this.store){
13468             this.store.afterCommit(this);
13469         }
13470     },
13471
13472     // private
13473     hasError : function(){
13474         return this.error != null;
13475     },
13476
13477     // private
13478     clearError : function(){
13479         this.error = null;
13480     },
13481
13482     /**
13483      * Creates a copy of this record.
13484      * @param {String} id (optional) A new record id if you don't want to use this record's id
13485      * @return {Record}
13486      */
13487     copy : function(newId) {
13488         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13489     }
13490 };/*
13491  * Based on:
13492  * Ext JS Library 1.1.1
13493  * Copyright(c) 2006-2007, Ext JS, LLC.
13494  *
13495  * Originally Released Under LGPL - original licence link has changed is not relivant.
13496  *
13497  * Fork - LGPL
13498  * <script type="text/javascript">
13499  */
13500
13501
13502
13503 /**
13504  * @class Roo.data.Store
13505  * @extends Roo.util.Observable
13506  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13507  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13508  * <p>
13509  * 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
13510  * has no knowledge of the format of the data returned by the Proxy.<br>
13511  * <p>
13512  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13513  * instances from the data object. These records are cached and made available through accessor functions.
13514  * @constructor
13515  * Creates a new Store.
13516  * @param {Object} config A config object containing the objects needed for the Store to access data,
13517  * and read the data into Records.
13518  */
13519 Roo.data.Store = function(config){
13520     this.data = new Roo.util.MixedCollection(false);
13521     this.data.getKey = function(o){
13522         return o.id;
13523     };
13524     this.baseParams = {};
13525     // private
13526     this.paramNames = {
13527         "start" : "start",
13528         "limit" : "limit",
13529         "sort" : "sort",
13530         "dir" : "dir",
13531         "multisort" : "_multisort"
13532     };
13533
13534     if(config && config.data){
13535         this.inlineData = config.data;
13536         delete config.data;
13537     }
13538
13539     Roo.apply(this, config);
13540     
13541     if(this.reader){ // reader passed
13542         this.reader = Roo.factory(this.reader, Roo.data);
13543         this.reader.xmodule = this.xmodule || false;
13544         if(!this.recordType){
13545             this.recordType = this.reader.recordType;
13546         }
13547         if(this.reader.onMetaChange){
13548             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13549         }
13550     }
13551
13552     if(this.recordType){
13553         this.fields = this.recordType.prototype.fields;
13554     }
13555     this.modified = [];
13556
13557     this.addEvents({
13558         /**
13559          * @event datachanged
13560          * Fires when the data cache has changed, and a widget which is using this Store
13561          * as a Record cache should refresh its view.
13562          * @param {Store} this
13563          */
13564         datachanged : true,
13565         /**
13566          * @event metachange
13567          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13568          * @param {Store} this
13569          * @param {Object} meta The JSON metadata
13570          */
13571         metachange : true,
13572         /**
13573          * @event add
13574          * Fires when Records have been added to the Store
13575          * @param {Store} this
13576          * @param {Roo.data.Record[]} records The array of Records added
13577          * @param {Number} index The index at which the record(s) were added
13578          */
13579         add : true,
13580         /**
13581          * @event remove
13582          * Fires when a Record has been removed from the Store
13583          * @param {Store} this
13584          * @param {Roo.data.Record} record The Record that was removed
13585          * @param {Number} index The index at which the record was removed
13586          */
13587         remove : true,
13588         /**
13589          * @event update
13590          * Fires when a Record has been updated
13591          * @param {Store} this
13592          * @param {Roo.data.Record} record The Record that was updated
13593          * @param {String} operation The update operation being performed.  Value may be one of:
13594          * <pre><code>
13595  Roo.data.Record.EDIT
13596  Roo.data.Record.REJECT
13597  Roo.data.Record.COMMIT
13598          * </code></pre>
13599          */
13600         update : true,
13601         /**
13602          * @event clear
13603          * Fires when the data cache has been cleared.
13604          * @param {Store} this
13605          */
13606         clear : true,
13607         /**
13608          * @event beforeload
13609          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13610          * the load action will be canceled.
13611          * @param {Store} this
13612          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13613          */
13614         beforeload : true,
13615         /**
13616          * @event beforeloadadd
13617          * Fires after a new set of Records has been loaded.
13618          * @param {Store} this
13619          * @param {Roo.data.Record[]} records The Records that were loaded
13620          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13621          */
13622         beforeloadadd : true,
13623         /**
13624          * @event load
13625          * Fires after a new set of Records has been loaded, before they are added to the store.
13626          * @param {Store} this
13627          * @param {Roo.data.Record[]} records The Records that were loaded
13628          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13629          * @params {Object} return from reader
13630          */
13631         load : true,
13632         /**
13633          * @event loadexception
13634          * Fires if an exception occurs in the Proxy during loading.
13635          * Called with the signature of the Proxy's "loadexception" event.
13636          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13637          * 
13638          * @param {Proxy} 
13639          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13640          * @param {Object} load options 
13641          * @param {Object} jsonData from your request (normally this contains the Exception)
13642          */
13643         loadexception : true
13644     });
13645     
13646     if(this.proxy){
13647         this.proxy = Roo.factory(this.proxy, Roo.data);
13648         this.proxy.xmodule = this.xmodule || false;
13649         this.relayEvents(this.proxy,  ["loadexception"]);
13650     }
13651     this.sortToggle = {};
13652     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13653
13654     Roo.data.Store.superclass.constructor.call(this);
13655
13656     if(this.inlineData){
13657         this.loadData(this.inlineData);
13658         delete this.inlineData;
13659     }
13660 };
13661
13662 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13663      /**
13664     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13665     * without a remote query - used by combo/forms at present.
13666     */
13667     
13668     /**
13669     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13670     */
13671     /**
13672     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13673     */
13674     /**
13675     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13676     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13677     */
13678     /**
13679     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13680     * on any HTTP request
13681     */
13682     /**
13683     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13684     */
13685     /**
13686     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13687     */
13688     multiSort: false,
13689     /**
13690     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13691     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13692     */
13693     remoteSort : false,
13694
13695     /**
13696     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13697      * loaded or when a record is removed. (defaults to false).
13698     */
13699     pruneModifiedRecords : false,
13700
13701     // private
13702     lastOptions : null,
13703
13704     /**
13705      * Add Records to the Store and fires the add event.
13706      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13707      */
13708     add : function(records){
13709         records = [].concat(records);
13710         for(var i = 0, len = records.length; i < len; i++){
13711             records[i].join(this);
13712         }
13713         var index = this.data.length;
13714         this.data.addAll(records);
13715         this.fireEvent("add", this, records, index);
13716     },
13717
13718     /**
13719      * Remove a Record from the Store and fires the remove event.
13720      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13721      */
13722     remove : function(record){
13723         var index = this.data.indexOf(record);
13724         this.data.removeAt(index);
13725  
13726         if(this.pruneModifiedRecords){
13727             this.modified.remove(record);
13728         }
13729         this.fireEvent("remove", this, record, index);
13730     },
13731
13732     /**
13733      * Remove all Records from the Store and fires the clear event.
13734      */
13735     removeAll : function(){
13736         this.data.clear();
13737         if(this.pruneModifiedRecords){
13738             this.modified = [];
13739         }
13740         this.fireEvent("clear", this);
13741     },
13742
13743     /**
13744      * Inserts Records to the Store at the given index and fires the add event.
13745      * @param {Number} index The start index at which to insert the passed Records.
13746      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13747      */
13748     insert : function(index, records){
13749         records = [].concat(records);
13750         for(var i = 0, len = records.length; i < len; i++){
13751             this.data.insert(index, records[i]);
13752             records[i].join(this);
13753         }
13754         this.fireEvent("add", this, records, index);
13755     },
13756
13757     /**
13758      * Get the index within the cache of the passed Record.
13759      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13760      * @return {Number} The index of the passed Record. Returns -1 if not found.
13761      */
13762     indexOf : function(record){
13763         return this.data.indexOf(record);
13764     },
13765
13766     /**
13767      * Get the index within the cache of the Record with the passed id.
13768      * @param {String} id The id of the Record to find.
13769      * @return {Number} The index of the Record. Returns -1 if not found.
13770      */
13771     indexOfId : function(id){
13772         return this.data.indexOfKey(id);
13773     },
13774
13775     /**
13776      * Get the Record with the specified id.
13777      * @param {String} id The id of the Record to find.
13778      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13779      */
13780     getById : function(id){
13781         return this.data.key(id);
13782     },
13783
13784     /**
13785      * Get the Record at the specified index.
13786      * @param {Number} index The index of the Record to find.
13787      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13788      */
13789     getAt : function(index){
13790         return this.data.itemAt(index);
13791     },
13792
13793     /**
13794      * Returns a range of Records between specified indices.
13795      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13796      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13797      * @return {Roo.data.Record[]} An array of Records
13798      */
13799     getRange : function(start, end){
13800         return this.data.getRange(start, end);
13801     },
13802
13803     // private
13804     storeOptions : function(o){
13805         o = Roo.apply({}, o);
13806         delete o.callback;
13807         delete o.scope;
13808         this.lastOptions = o;
13809     },
13810
13811     /**
13812      * Loads the Record cache from the configured Proxy using the configured Reader.
13813      * <p>
13814      * If using remote paging, then the first load call must specify the <em>start</em>
13815      * and <em>limit</em> properties in the options.params property to establish the initial
13816      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13817      * <p>
13818      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13819      * and this call will return before the new data has been loaded. Perform any post-processing
13820      * in a callback function, or in a "load" event handler.</strong>
13821      * <p>
13822      * @param {Object} options An object containing properties which control loading options:<ul>
13823      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13824      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13825      * passed the following arguments:<ul>
13826      * <li>r : Roo.data.Record[]</li>
13827      * <li>options: Options object from the load call</li>
13828      * <li>success: Boolean success indicator</li></ul></li>
13829      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13830      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13831      * </ul>
13832      */
13833     load : function(options){
13834         options = options || {};
13835         if(this.fireEvent("beforeload", this, options) !== false){
13836             this.storeOptions(options);
13837             var p = Roo.apply(options.params || {}, this.baseParams);
13838             // if meta was not loaded from remote source.. try requesting it.
13839             if (!this.reader.metaFromRemote) {
13840                 p._requestMeta = 1;
13841             }
13842             if(this.sortInfo && this.remoteSort){
13843                 var pn = this.paramNames;
13844                 p[pn["sort"]] = this.sortInfo.field;
13845                 p[pn["dir"]] = this.sortInfo.direction;
13846             }
13847             if (this.multiSort) {
13848                 var pn = this.paramNames;
13849                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13850             }
13851             
13852             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13853         }
13854     },
13855
13856     /**
13857      * Reloads the Record cache from the configured Proxy using the configured Reader and
13858      * the options from the last load operation performed.
13859      * @param {Object} options (optional) An object containing properties which may override the options
13860      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13861      * the most recently used options are reused).
13862      */
13863     reload : function(options){
13864         this.load(Roo.applyIf(options||{}, this.lastOptions));
13865     },
13866
13867     // private
13868     // Called as a callback by the Reader during a load operation.
13869     loadRecords : function(o, options, success){
13870         if(!o || success === false){
13871             if(success !== false){
13872                 this.fireEvent("load", this, [], options, o);
13873             }
13874             if(options.callback){
13875                 options.callback.call(options.scope || this, [], options, false);
13876             }
13877             return;
13878         }
13879         // if data returned failure - throw an exception.
13880         if (o.success === false) {
13881             // show a message if no listener is registered.
13882             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13883                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13884             }
13885             // loadmask wil be hooked into this..
13886             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13887             return;
13888         }
13889         var r = o.records, t = o.totalRecords || r.length;
13890         
13891         this.fireEvent("beforeloadadd", this, r, options, o);
13892         
13893         if(!options || options.add !== true){
13894             if(this.pruneModifiedRecords){
13895                 this.modified = [];
13896             }
13897             for(var i = 0, len = r.length; i < len; i++){
13898                 r[i].join(this);
13899             }
13900             if(this.snapshot){
13901                 this.data = this.snapshot;
13902                 delete this.snapshot;
13903             }
13904             this.data.clear();
13905             this.data.addAll(r);
13906             this.totalLength = t;
13907             this.applySort();
13908             this.fireEvent("datachanged", this);
13909         }else{
13910             this.totalLength = Math.max(t, this.data.length+r.length);
13911             this.add(r);
13912         }
13913         
13914         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13915                 
13916             var e = new Roo.data.Record({});
13917
13918             e.set(this.parent.displayField, this.parent.emptyTitle);
13919             e.set(this.parent.valueField, '');
13920
13921             this.insert(0, e);
13922         }
13923             
13924         this.fireEvent("load", this, r, options, o);
13925         if(options.callback){
13926             options.callback.call(options.scope || this, r, options, true);
13927         }
13928     },
13929
13930
13931     /**
13932      * Loads data from a passed data block. A Reader which understands the format of the data
13933      * must have been configured in the constructor.
13934      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13935      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13936      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13937      */
13938     loadData : function(o, append){
13939         var r = this.reader.readRecords(o);
13940         this.loadRecords(r, {add: append}, true);
13941     },
13942     
13943      /**
13944      * using 'cn' the nested child reader read the child array into it's child stores.
13945      * @param {Object} rec The record with a 'children array
13946      */
13947     loadDataFromChildren : function(rec)
13948     {
13949         this.loadData(this.reader.toLoadData(rec));
13950     },
13951     
13952
13953     /**
13954      * Gets the number of cached records.
13955      * <p>
13956      * <em>If using paging, this may not be the total size of the dataset. If the data object
13957      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13958      * the data set size</em>
13959      */
13960     getCount : function(){
13961         return this.data.length || 0;
13962     },
13963
13964     /**
13965      * Gets the total number of records in the dataset as returned by the server.
13966      * <p>
13967      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13968      * the dataset size</em>
13969      */
13970     getTotalCount : function(){
13971         return this.totalLength || 0;
13972     },
13973
13974     /**
13975      * Returns the sort state of the Store as an object with two properties:
13976      * <pre><code>
13977  field {String} The name of the field by which the Records are sorted
13978  direction {String} The sort order, "ASC" or "DESC"
13979      * </code></pre>
13980      */
13981     getSortState : function(){
13982         return this.sortInfo;
13983     },
13984
13985     // private
13986     applySort : function(){
13987         if(this.sortInfo && !this.remoteSort){
13988             var s = this.sortInfo, f = s.field;
13989             var st = this.fields.get(f).sortType;
13990             var fn = function(r1, r2){
13991                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13992                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13993             };
13994             this.data.sort(s.direction, fn);
13995             if(this.snapshot && this.snapshot != this.data){
13996                 this.snapshot.sort(s.direction, fn);
13997             }
13998         }
13999     },
14000
14001     /**
14002      * Sets the default sort column and order to be used by the next load operation.
14003      * @param {String} fieldName The name of the field to sort by.
14004      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14005      */
14006     setDefaultSort : function(field, dir){
14007         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14008     },
14009
14010     /**
14011      * Sort the Records.
14012      * If remote sorting is used, the sort is performed on the server, and the cache is
14013      * reloaded. If local sorting is used, the cache is sorted internally.
14014      * @param {String} fieldName The name of the field to sort by.
14015      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14016      */
14017     sort : function(fieldName, dir){
14018         var f = this.fields.get(fieldName);
14019         if(!dir){
14020             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14021             
14022             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14023                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14024             }else{
14025                 dir = f.sortDir;
14026             }
14027         }
14028         this.sortToggle[f.name] = dir;
14029         this.sortInfo = {field: f.name, direction: dir};
14030         if(!this.remoteSort){
14031             this.applySort();
14032             this.fireEvent("datachanged", this);
14033         }else{
14034             this.load(this.lastOptions);
14035         }
14036     },
14037
14038     /**
14039      * Calls the specified function for each of the Records in the cache.
14040      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14041      * Returning <em>false</em> aborts and exits the iteration.
14042      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14043      */
14044     each : function(fn, scope){
14045         this.data.each(fn, scope);
14046     },
14047
14048     /**
14049      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14050      * (e.g., during paging).
14051      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14052      */
14053     getModifiedRecords : function(){
14054         return this.modified;
14055     },
14056
14057     // private
14058     createFilterFn : function(property, value, anyMatch){
14059         if(!value.exec){ // not a regex
14060             value = String(value);
14061             if(value.length == 0){
14062                 return false;
14063             }
14064             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14065         }
14066         return function(r){
14067             return value.test(r.data[property]);
14068         };
14069     },
14070
14071     /**
14072      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14073      * @param {String} property A field on your records
14074      * @param {Number} start The record index to start at (defaults to 0)
14075      * @param {Number} end The last record index to include (defaults to length - 1)
14076      * @return {Number} The sum
14077      */
14078     sum : function(property, start, end){
14079         var rs = this.data.items, v = 0;
14080         start = start || 0;
14081         end = (end || end === 0) ? end : rs.length-1;
14082
14083         for(var i = start; i <= end; i++){
14084             v += (rs[i].data[property] || 0);
14085         }
14086         return v;
14087     },
14088
14089     /**
14090      * Filter the records by a specified property.
14091      * @param {String} field A field on your records
14092      * @param {String/RegExp} value Either a string that the field
14093      * should start with or a RegExp to test against the field
14094      * @param {Boolean} anyMatch True to match any part not just the beginning
14095      */
14096     filter : function(property, value, anyMatch){
14097         var fn = this.createFilterFn(property, value, anyMatch);
14098         return fn ? this.filterBy(fn) : this.clearFilter();
14099     },
14100
14101     /**
14102      * Filter by a function. The specified function will be called with each
14103      * record in this data source. If the function returns true the record is included,
14104      * otherwise it is filtered.
14105      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14106      * @param {Object} scope (optional) The scope of the function (defaults to this)
14107      */
14108     filterBy : function(fn, scope){
14109         this.snapshot = this.snapshot || this.data;
14110         this.data = this.queryBy(fn, scope||this);
14111         this.fireEvent("datachanged", this);
14112     },
14113
14114     /**
14115      * Query the records by a specified property.
14116      * @param {String} field A field on your records
14117      * @param {String/RegExp} value Either a string that the field
14118      * should start with or a RegExp to test against the field
14119      * @param {Boolean} anyMatch True to match any part not just the beginning
14120      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14121      */
14122     query : function(property, value, anyMatch){
14123         var fn = this.createFilterFn(property, value, anyMatch);
14124         return fn ? this.queryBy(fn) : this.data.clone();
14125     },
14126
14127     /**
14128      * Query by a function. The specified function will be called with each
14129      * record in this data source. If the function returns true the record is included
14130      * in the results.
14131      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14132      * @param {Object} scope (optional) The scope of the function (defaults to this)
14133       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14134      **/
14135     queryBy : function(fn, scope){
14136         var data = this.snapshot || this.data;
14137         return data.filterBy(fn, scope||this);
14138     },
14139
14140     /**
14141      * Collects unique values for a particular dataIndex from this store.
14142      * @param {String} dataIndex The property to collect
14143      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14144      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14145      * @return {Array} An array of the unique values
14146      **/
14147     collect : function(dataIndex, allowNull, bypassFilter){
14148         var d = (bypassFilter === true && this.snapshot) ?
14149                 this.snapshot.items : this.data.items;
14150         var v, sv, r = [], l = {};
14151         for(var i = 0, len = d.length; i < len; i++){
14152             v = d[i].data[dataIndex];
14153             sv = String(v);
14154             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14155                 l[sv] = true;
14156                 r[r.length] = v;
14157             }
14158         }
14159         return r;
14160     },
14161
14162     /**
14163      * Revert to a view of the Record cache with no filtering applied.
14164      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14165      */
14166     clearFilter : function(suppressEvent){
14167         if(this.snapshot && this.snapshot != this.data){
14168             this.data = this.snapshot;
14169             delete this.snapshot;
14170             if(suppressEvent !== true){
14171                 this.fireEvent("datachanged", this);
14172             }
14173         }
14174     },
14175
14176     // private
14177     afterEdit : function(record){
14178         if(this.modified.indexOf(record) == -1){
14179             this.modified.push(record);
14180         }
14181         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14182     },
14183     
14184     // private
14185     afterReject : function(record){
14186         this.modified.remove(record);
14187         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14188     },
14189
14190     // private
14191     afterCommit : function(record){
14192         this.modified.remove(record);
14193         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14194     },
14195
14196     /**
14197      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14198      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14199      */
14200     commitChanges : function(){
14201         var m = this.modified.slice(0);
14202         this.modified = [];
14203         for(var i = 0, len = m.length; i < len; i++){
14204             m[i].commit();
14205         }
14206     },
14207
14208     /**
14209      * Cancel outstanding changes on all changed records.
14210      */
14211     rejectChanges : function(){
14212         var m = this.modified.slice(0);
14213         this.modified = [];
14214         for(var i = 0, len = m.length; i < len; i++){
14215             m[i].reject();
14216         }
14217     },
14218
14219     onMetaChange : function(meta, rtype, o){
14220         this.recordType = rtype;
14221         this.fields = rtype.prototype.fields;
14222         delete this.snapshot;
14223         this.sortInfo = meta.sortInfo || this.sortInfo;
14224         this.modified = [];
14225         this.fireEvent('metachange', this, this.reader.meta);
14226     },
14227     
14228     moveIndex : function(data, type)
14229     {
14230         var index = this.indexOf(data);
14231         
14232         var newIndex = index + type;
14233         
14234         this.remove(data);
14235         
14236         this.insert(newIndex, data);
14237         
14238     }
14239 });/*
14240  * Based on:
14241  * Ext JS Library 1.1.1
14242  * Copyright(c) 2006-2007, Ext JS, LLC.
14243  *
14244  * Originally Released Under LGPL - original licence link has changed is not relivant.
14245  *
14246  * Fork - LGPL
14247  * <script type="text/javascript">
14248  */
14249
14250 /**
14251  * @class Roo.data.SimpleStore
14252  * @extends Roo.data.Store
14253  * Small helper class to make creating Stores from Array data easier.
14254  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14255  * @cfg {Array} fields An array of field definition objects, or field name strings.
14256  * @cfg {Object} an existing reader (eg. copied from another store)
14257  * @cfg {Array} data The multi-dimensional array of data
14258  * @constructor
14259  * @param {Object} config
14260  */
14261 Roo.data.SimpleStore = function(config)
14262 {
14263     Roo.data.SimpleStore.superclass.constructor.call(this, {
14264         isLocal : true,
14265         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14266                 id: config.id
14267             },
14268             Roo.data.Record.create(config.fields)
14269         ),
14270         proxy : new Roo.data.MemoryProxy(config.data)
14271     });
14272     this.load();
14273 };
14274 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14275  * Based on:
14276  * Ext JS Library 1.1.1
14277  * Copyright(c) 2006-2007, Ext JS, LLC.
14278  *
14279  * Originally Released Under LGPL - original licence link has changed is not relivant.
14280  *
14281  * Fork - LGPL
14282  * <script type="text/javascript">
14283  */
14284
14285 /**
14286 /**
14287  * @extends Roo.data.Store
14288  * @class Roo.data.JsonStore
14289  * Small helper class to make creating Stores for JSON data easier. <br/>
14290 <pre><code>
14291 var store = new Roo.data.JsonStore({
14292     url: 'get-images.php',
14293     root: 'images',
14294     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14295 });
14296 </code></pre>
14297  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14298  * JsonReader and HttpProxy (unless inline data is provided).</b>
14299  * @cfg {Array} fields An array of field definition objects, or field name strings.
14300  * @constructor
14301  * @param {Object} config
14302  */
14303 Roo.data.JsonStore = function(c){
14304     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14305         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14306         reader: new Roo.data.JsonReader(c, c.fields)
14307     }));
14308 };
14309 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14310  * Based on:
14311  * Ext JS Library 1.1.1
14312  * Copyright(c) 2006-2007, Ext JS, LLC.
14313  *
14314  * Originally Released Under LGPL - original licence link has changed is not relivant.
14315  *
14316  * Fork - LGPL
14317  * <script type="text/javascript">
14318  */
14319
14320  
14321 Roo.data.Field = function(config){
14322     if(typeof config == "string"){
14323         config = {name: config};
14324     }
14325     Roo.apply(this, config);
14326     
14327     if(!this.type){
14328         this.type = "auto";
14329     }
14330     
14331     var st = Roo.data.SortTypes;
14332     // named sortTypes are supported, here we look them up
14333     if(typeof this.sortType == "string"){
14334         this.sortType = st[this.sortType];
14335     }
14336     
14337     // set default sortType for strings and dates
14338     if(!this.sortType){
14339         switch(this.type){
14340             case "string":
14341                 this.sortType = st.asUCString;
14342                 break;
14343             case "date":
14344                 this.sortType = st.asDate;
14345                 break;
14346             default:
14347                 this.sortType = st.none;
14348         }
14349     }
14350
14351     // define once
14352     var stripRe = /[\$,%]/g;
14353
14354     // prebuilt conversion function for this field, instead of
14355     // switching every time we're reading a value
14356     if(!this.convert){
14357         var cv, dateFormat = this.dateFormat;
14358         switch(this.type){
14359             case "":
14360             case "auto":
14361             case undefined:
14362                 cv = function(v){ return v; };
14363                 break;
14364             case "string":
14365                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14366                 break;
14367             case "int":
14368                 cv = function(v){
14369                     return v !== undefined && v !== null && v !== '' ?
14370                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14371                     };
14372                 break;
14373             case "float":
14374                 cv = function(v){
14375                     return v !== undefined && v !== null && v !== '' ?
14376                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14377                     };
14378                 break;
14379             case "bool":
14380             case "boolean":
14381                 cv = function(v){ return v === true || v === "true" || v == 1; };
14382                 break;
14383             case "date":
14384                 cv = function(v){
14385                     if(!v){
14386                         return '';
14387                     }
14388                     if(v instanceof Date){
14389                         return v;
14390                     }
14391                     if(dateFormat){
14392                         if(dateFormat == "timestamp"){
14393                             return new Date(v*1000);
14394                         }
14395                         return Date.parseDate(v, dateFormat);
14396                     }
14397                     var parsed = Date.parse(v);
14398                     return parsed ? new Date(parsed) : null;
14399                 };
14400              break;
14401             
14402         }
14403         this.convert = cv;
14404     }
14405 };
14406
14407 Roo.data.Field.prototype = {
14408     dateFormat: null,
14409     defaultValue: "",
14410     mapping: null,
14411     sortType : null,
14412     sortDir : "ASC"
14413 };/*
14414  * Based on:
14415  * Ext JS Library 1.1.1
14416  * Copyright(c) 2006-2007, Ext JS, LLC.
14417  *
14418  * Originally Released Under LGPL - original licence link has changed is not relivant.
14419  *
14420  * Fork - LGPL
14421  * <script type="text/javascript">
14422  */
14423  
14424 // Base class for reading structured data from a data source.  This class is intended to be
14425 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14426
14427 /**
14428  * @class Roo.data.DataReader
14429  * Base class for reading structured data from a data source.  This class is intended to be
14430  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14431  */
14432
14433 Roo.data.DataReader = function(meta, recordType){
14434     
14435     this.meta = meta;
14436     
14437     this.recordType = recordType instanceof Array ? 
14438         Roo.data.Record.create(recordType) : recordType;
14439 };
14440
14441 Roo.data.DataReader.prototype = {
14442     
14443     
14444     readerType : 'Data',
14445      /**
14446      * Create an empty record
14447      * @param {Object} data (optional) - overlay some values
14448      * @return {Roo.data.Record} record created.
14449      */
14450     newRow :  function(d) {
14451         var da =  {};
14452         this.recordType.prototype.fields.each(function(c) {
14453             switch( c.type) {
14454                 case 'int' : da[c.name] = 0; break;
14455                 case 'date' : da[c.name] = new Date(); break;
14456                 case 'float' : da[c.name] = 0.0; break;
14457                 case 'boolean' : da[c.name] = false; break;
14458                 default : da[c.name] = ""; break;
14459             }
14460             
14461         });
14462         return new this.recordType(Roo.apply(da, d));
14463     }
14464     
14465     
14466 };/*
14467  * Based on:
14468  * Ext JS Library 1.1.1
14469  * Copyright(c) 2006-2007, Ext JS, LLC.
14470  *
14471  * Originally Released Under LGPL - original licence link has changed is not relivant.
14472  *
14473  * Fork - LGPL
14474  * <script type="text/javascript">
14475  */
14476
14477 /**
14478  * @class Roo.data.DataProxy
14479  * @extends Roo.data.Observable
14480  * This class is an abstract base class for implementations which provide retrieval of
14481  * unformatted data objects.<br>
14482  * <p>
14483  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14484  * (of the appropriate type which knows how to parse the data object) to provide a block of
14485  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14486  * <p>
14487  * Custom implementations must implement the load method as described in
14488  * {@link Roo.data.HttpProxy#load}.
14489  */
14490 Roo.data.DataProxy = function(){
14491     this.addEvents({
14492         /**
14493          * @event beforeload
14494          * Fires before a network request is made to retrieve a data object.
14495          * @param {Object} This DataProxy object.
14496          * @param {Object} params The params parameter to the load function.
14497          */
14498         beforeload : true,
14499         /**
14500          * @event load
14501          * Fires before the load method's callback is called.
14502          * @param {Object} This DataProxy object.
14503          * @param {Object} o The data object.
14504          * @param {Object} arg The callback argument object passed to the load function.
14505          */
14506         load : true,
14507         /**
14508          * @event loadexception
14509          * Fires if an Exception occurs during data retrieval.
14510          * @param {Object} This DataProxy object.
14511          * @param {Object} o The data object.
14512          * @param {Object} arg The callback argument object passed to the load function.
14513          * @param {Object} e The Exception.
14514          */
14515         loadexception : true
14516     });
14517     Roo.data.DataProxy.superclass.constructor.call(this);
14518 };
14519
14520 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14521
14522     /**
14523      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14524      */
14525 /*
14526  * Based on:
14527  * Ext JS Library 1.1.1
14528  * Copyright(c) 2006-2007, Ext JS, LLC.
14529  *
14530  * Originally Released Under LGPL - original licence link has changed is not relivant.
14531  *
14532  * Fork - LGPL
14533  * <script type="text/javascript">
14534  */
14535 /**
14536  * @class Roo.data.MemoryProxy
14537  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14538  * to the Reader when its load method is called.
14539  * @constructor
14540  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14541  */
14542 Roo.data.MemoryProxy = function(data){
14543     if (data.data) {
14544         data = data.data;
14545     }
14546     Roo.data.MemoryProxy.superclass.constructor.call(this);
14547     this.data = data;
14548 };
14549
14550 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14551     
14552     /**
14553      * Load data from the requested source (in this case an in-memory
14554      * data object passed to the constructor), read the data object into
14555      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14556      * process that block using the passed callback.
14557      * @param {Object} params This parameter is not used by the MemoryProxy class.
14558      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14559      * object into a block of Roo.data.Records.
14560      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14561      * The function must be passed <ul>
14562      * <li>The Record block object</li>
14563      * <li>The "arg" argument from the load function</li>
14564      * <li>A boolean success indicator</li>
14565      * </ul>
14566      * @param {Object} scope The scope in which to call the callback
14567      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14568      */
14569     load : function(params, reader, callback, scope, arg){
14570         params = params || {};
14571         var result;
14572         try {
14573             result = reader.readRecords(params.data ? params.data :this.data);
14574         }catch(e){
14575             this.fireEvent("loadexception", this, arg, null, e);
14576             callback.call(scope, null, arg, false);
14577             return;
14578         }
14579         callback.call(scope, result, arg, true);
14580     },
14581     
14582     // private
14583     update : function(params, records){
14584         
14585     }
14586 });/*
14587  * Based on:
14588  * Ext JS Library 1.1.1
14589  * Copyright(c) 2006-2007, Ext JS, LLC.
14590  *
14591  * Originally Released Under LGPL - original licence link has changed is not relivant.
14592  *
14593  * Fork - LGPL
14594  * <script type="text/javascript">
14595  */
14596 /**
14597  * @class Roo.data.HttpProxy
14598  * @extends Roo.data.DataProxy
14599  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14600  * configured to reference a certain URL.<br><br>
14601  * <p>
14602  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14603  * from which the running page was served.<br><br>
14604  * <p>
14605  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14606  * <p>
14607  * Be aware that to enable the browser to parse an XML document, the server must set
14608  * the Content-Type header in the HTTP response to "text/xml".
14609  * @constructor
14610  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14611  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14612  * will be used to make the request.
14613  */
14614 Roo.data.HttpProxy = function(conn){
14615     Roo.data.HttpProxy.superclass.constructor.call(this);
14616     // is conn a conn config or a real conn?
14617     this.conn = conn;
14618     this.useAjax = !conn || !conn.events;
14619   
14620 };
14621
14622 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14623     // thse are take from connection...
14624     
14625     /**
14626      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14627      */
14628     /**
14629      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14630      * extra parameters to each request made by this object. (defaults to undefined)
14631      */
14632     /**
14633      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14634      *  to each request made by this object. (defaults to undefined)
14635      */
14636     /**
14637      * @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)
14638      */
14639     /**
14640      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14641      */
14642      /**
14643      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14644      * @type Boolean
14645      */
14646   
14647
14648     /**
14649      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14650      * @type Boolean
14651      */
14652     /**
14653      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14654      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14655      * a finer-grained basis than the DataProxy events.
14656      */
14657     getConnection : function(){
14658         return this.useAjax ? Roo.Ajax : this.conn;
14659     },
14660
14661     /**
14662      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14663      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14664      * process that block using the passed callback.
14665      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14666      * for the request to the remote server.
14667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14668      * object into a block of Roo.data.Records.
14669      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14670      * The function must be passed <ul>
14671      * <li>The Record block object</li>
14672      * <li>The "arg" argument from the load function</li>
14673      * <li>A boolean success indicator</li>
14674      * </ul>
14675      * @param {Object} scope The scope in which to call the callback
14676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14677      */
14678     load : function(params, reader, callback, scope, arg){
14679         if(this.fireEvent("beforeload", this, params) !== false){
14680             var  o = {
14681                 params : params || {},
14682                 request: {
14683                     callback : callback,
14684                     scope : scope,
14685                     arg : arg
14686                 },
14687                 reader: reader,
14688                 callback : this.loadResponse,
14689                 scope: this
14690             };
14691             if(this.useAjax){
14692                 Roo.applyIf(o, this.conn);
14693                 if(this.activeRequest){
14694                     Roo.Ajax.abort(this.activeRequest);
14695                 }
14696                 this.activeRequest = Roo.Ajax.request(o);
14697             }else{
14698                 this.conn.request(o);
14699             }
14700         }else{
14701             callback.call(scope||this, null, arg, false);
14702         }
14703     },
14704
14705     // private
14706     loadResponse : function(o, success, response){
14707         delete this.activeRequest;
14708         if(!success){
14709             this.fireEvent("loadexception", this, o, response);
14710             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14711             return;
14712         }
14713         var result;
14714         try {
14715             result = o.reader.read(response);
14716         }catch(e){
14717             this.fireEvent("loadexception", this, o, response, e);
14718             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14719             return;
14720         }
14721         
14722         this.fireEvent("load", this, o, o.request.arg);
14723         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14724     },
14725
14726     // private
14727     update : function(dataSet){
14728
14729     },
14730
14731     // private
14732     updateResponse : function(dataSet){
14733
14734     }
14735 });/*
14736  * Based on:
14737  * Ext JS Library 1.1.1
14738  * Copyright(c) 2006-2007, Ext JS, LLC.
14739  *
14740  * Originally Released Under LGPL - original licence link has changed is not relivant.
14741  *
14742  * Fork - LGPL
14743  * <script type="text/javascript">
14744  */
14745
14746 /**
14747  * @class Roo.data.ScriptTagProxy
14748  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14749  * other than the originating domain of the running page.<br><br>
14750  * <p>
14751  * <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
14752  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14753  * <p>
14754  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14755  * source code that is used as the source inside a &lt;script> tag.<br><br>
14756  * <p>
14757  * In order for the browser to process the returned data, the server must wrap the data object
14758  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14759  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14760  * depending on whether the callback name was passed:
14761  * <p>
14762  * <pre><code>
14763 boolean scriptTag = false;
14764 String cb = request.getParameter("callback");
14765 if (cb != null) {
14766     scriptTag = true;
14767     response.setContentType("text/javascript");
14768 } else {
14769     response.setContentType("application/x-json");
14770 }
14771 Writer out = response.getWriter();
14772 if (scriptTag) {
14773     out.write(cb + "(");
14774 }
14775 out.print(dataBlock.toJsonString());
14776 if (scriptTag) {
14777     out.write(");");
14778 }
14779 </pre></code>
14780  *
14781  * @constructor
14782  * @param {Object} config A configuration object.
14783  */
14784 Roo.data.ScriptTagProxy = function(config){
14785     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14786     Roo.apply(this, config);
14787     this.head = document.getElementsByTagName("head")[0];
14788 };
14789
14790 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14791
14792 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14793     /**
14794      * @cfg {String} url The URL from which to request the data object.
14795      */
14796     /**
14797      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14798      */
14799     timeout : 30000,
14800     /**
14801      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14802      * the server the name of the callback function set up by the load call to process the returned data object.
14803      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14804      * javascript output which calls this named function passing the data object as its only parameter.
14805      */
14806     callbackParam : "callback",
14807     /**
14808      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14809      * name to the request.
14810      */
14811     nocache : true,
14812
14813     /**
14814      * Load data from the configured URL, read the data object into
14815      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14816      * process that block using the passed callback.
14817      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14818      * for the request to the remote server.
14819      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14820      * object into a block of Roo.data.Records.
14821      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14822      * The function must be passed <ul>
14823      * <li>The Record block object</li>
14824      * <li>The "arg" argument from the load function</li>
14825      * <li>A boolean success indicator</li>
14826      * </ul>
14827      * @param {Object} scope The scope in which to call the callback
14828      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14829      */
14830     load : function(params, reader, callback, scope, arg){
14831         if(this.fireEvent("beforeload", this, params) !== false){
14832
14833             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14834
14835             var url = this.url;
14836             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14837             if(this.nocache){
14838                 url += "&_dc=" + (new Date().getTime());
14839             }
14840             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14841             var trans = {
14842                 id : transId,
14843                 cb : "stcCallback"+transId,
14844                 scriptId : "stcScript"+transId,
14845                 params : params,
14846                 arg : arg,
14847                 url : url,
14848                 callback : callback,
14849                 scope : scope,
14850                 reader : reader
14851             };
14852             var conn = this;
14853
14854             window[trans.cb] = function(o){
14855                 conn.handleResponse(o, trans);
14856             };
14857
14858             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14859
14860             if(this.autoAbort !== false){
14861                 this.abort();
14862             }
14863
14864             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14865
14866             var script = document.createElement("script");
14867             script.setAttribute("src", url);
14868             script.setAttribute("type", "text/javascript");
14869             script.setAttribute("id", trans.scriptId);
14870             this.head.appendChild(script);
14871
14872             this.trans = trans;
14873         }else{
14874             callback.call(scope||this, null, arg, false);
14875         }
14876     },
14877
14878     // private
14879     isLoading : function(){
14880         return this.trans ? true : false;
14881     },
14882
14883     /**
14884      * Abort the current server request.
14885      */
14886     abort : function(){
14887         if(this.isLoading()){
14888             this.destroyTrans(this.trans);
14889         }
14890     },
14891
14892     // private
14893     destroyTrans : function(trans, isLoaded){
14894         this.head.removeChild(document.getElementById(trans.scriptId));
14895         clearTimeout(trans.timeoutId);
14896         if(isLoaded){
14897             window[trans.cb] = undefined;
14898             try{
14899                 delete window[trans.cb];
14900             }catch(e){}
14901         }else{
14902             // if hasn't been loaded, wait for load to remove it to prevent script error
14903             window[trans.cb] = function(){
14904                 window[trans.cb] = undefined;
14905                 try{
14906                     delete window[trans.cb];
14907                 }catch(e){}
14908             };
14909         }
14910     },
14911
14912     // private
14913     handleResponse : function(o, trans){
14914         this.trans = false;
14915         this.destroyTrans(trans, true);
14916         var result;
14917         try {
14918             result = trans.reader.readRecords(o);
14919         }catch(e){
14920             this.fireEvent("loadexception", this, o, trans.arg, e);
14921             trans.callback.call(trans.scope||window, null, trans.arg, false);
14922             return;
14923         }
14924         this.fireEvent("load", this, o, trans.arg);
14925         trans.callback.call(trans.scope||window, result, trans.arg, true);
14926     },
14927
14928     // private
14929     handleFailure : function(trans){
14930         this.trans = false;
14931         this.destroyTrans(trans, false);
14932         this.fireEvent("loadexception", this, null, trans.arg);
14933         trans.callback.call(trans.scope||window, null, trans.arg, false);
14934     }
14935 });/*
14936  * Based on:
14937  * Ext JS Library 1.1.1
14938  * Copyright(c) 2006-2007, Ext JS, LLC.
14939  *
14940  * Originally Released Under LGPL - original licence link has changed is not relivant.
14941  *
14942  * Fork - LGPL
14943  * <script type="text/javascript">
14944  */
14945
14946 /**
14947  * @class Roo.data.JsonReader
14948  * @extends Roo.data.DataReader
14949  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14950  * based on mappings in a provided Roo.data.Record constructor.
14951  * 
14952  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14953  * in the reply previously. 
14954  * 
14955  * <p>
14956  * Example code:
14957  * <pre><code>
14958 var RecordDef = Roo.data.Record.create([
14959     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14960     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14961 ]);
14962 var myReader = new Roo.data.JsonReader({
14963     totalProperty: "results",    // The property which contains the total dataset size (optional)
14964     root: "rows",                // The property which contains an Array of row objects
14965     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14966 }, RecordDef);
14967 </code></pre>
14968  * <p>
14969  * This would consume a JSON file like this:
14970  * <pre><code>
14971 { 'results': 2, 'rows': [
14972     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14973     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14974 }
14975 </code></pre>
14976  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14977  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14978  * paged from the remote server.
14979  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14980  * @cfg {String} root name of the property which contains the Array of row objects.
14981  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14982  * @cfg {Array} fields Array of field definition objects
14983  * @constructor
14984  * Create a new JsonReader
14985  * @param {Object} meta Metadata configuration options
14986  * @param {Object} recordType Either an Array of field definition objects,
14987  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14988  */
14989 Roo.data.JsonReader = function(meta, recordType){
14990     
14991     meta = meta || {};
14992     // set some defaults:
14993     Roo.applyIf(meta, {
14994         totalProperty: 'total',
14995         successProperty : 'success',
14996         root : 'data',
14997         id : 'id'
14998     });
14999     
15000     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15001 };
15002 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15003     
15004     readerType : 'Json',
15005     
15006     /**
15007      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15008      * Used by Store query builder to append _requestMeta to params.
15009      * 
15010      */
15011     metaFromRemote : false,
15012     /**
15013      * This method is only used by a DataProxy which has retrieved data from a remote server.
15014      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15015      * @return {Object} data A data block which is used by an Roo.data.Store object as
15016      * a cache of Roo.data.Records.
15017      */
15018     read : function(response){
15019         var json = response.responseText;
15020        
15021         var o = /* eval:var:o */ eval("("+json+")");
15022         if(!o) {
15023             throw {message: "JsonReader.read: Json object not found"};
15024         }
15025         
15026         if(o.metaData){
15027             
15028             delete this.ef;
15029             this.metaFromRemote = true;
15030             this.meta = o.metaData;
15031             this.recordType = Roo.data.Record.create(o.metaData.fields);
15032             this.onMetaChange(this.meta, this.recordType, o);
15033         }
15034         return this.readRecords(o);
15035     },
15036
15037     // private function a store will implement
15038     onMetaChange : function(meta, recordType, o){
15039
15040     },
15041
15042     /**
15043          * @ignore
15044          */
15045     simpleAccess: function(obj, subsc) {
15046         return obj[subsc];
15047     },
15048
15049         /**
15050          * @ignore
15051          */
15052     getJsonAccessor: function(){
15053         var re = /[\[\.]/;
15054         return function(expr) {
15055             try {
15056                 return(re.test(expr))
15057                     ? new Function("obj", "return obj." + expr)
15058                     : function(obj){
15059                         return obj[expr];
15060                     };
15061             } catch(e){}
15062             return Roo.emptyFn;
15063         };
15064     }(),
15065
15066     /**
15067      * Create a data block containing Roo.data.Records from an XML document.
15068      * @param {Object} o An object which contains an Array of row objects in the property specified
15069      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15070      * which contains the total size of the dataset.
15071      * @return {Object} data A data block which is used by an Roo.data.Store object as
15072      * a cache of Roo.data.Records.
15073      */
15074     readRecords : function(o){
15075         /**
15076          * After any data loads, the raw JSON data is available for further custom processing.
15077          * @type Object
15078          */
15079         this.o = o;
15080         var s = this.meta, Record = this.recordType,
15081             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15082
15083 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15084         if (!this.ef) {
15085             if(s.totalProperty) {
15086                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15087                 }
15088                 if(s.successProperty) {
15089                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15090                 }
15091                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15092                 if (s.id) {
15093                         var g = this.getJsonAccessor(s.id);
15094                         this.getId = function(rec) {
15095                                 var r = g(rec);  
15096                                 return (r === undefined || r === "") ? null : r;
15097                         };
15098                 } else {
15099                         this.getId = function(){return null;};
15100                 }
15101             this.ef = [];
15102             for(var jj = 0; jj < fl; jj++){
15103                 f = fi[jj];
15104                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15105                 this.ef[jj] = this.getJsonAccessor(map);
15106             }
15107         }
15108
15109         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15110         if(s.totalProperty){
15111             var vt = parseInt(this.getTotal(o), 10);
15112             if(!isNaN(vt)){
15113                 totalRecords = vt;
15114             }
15115         }
15116         if(s.successProperty){
15117             var vs = this.getSuccess(o);
15118             if(vs === false || vs === 'false'){
15119                 success = false;
15120             }
15121         }
15122         var records = [];
15123         for(var i = 0; i < c; i++){
15124                 var n = root[i];
15125             var values = {};
15126             var id = this.getId(n);
15127             for(var j = 0; j < fl; j++){
15128                 f = fi[j];
15129             var v = this.ef[j](n);
15130             if (!f.convert) {
15131                 Roo.log('missing convert for ' + f.name);
15132                 Roo.log(f);
15133                 continue;
15134             }
15135             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15136             }
15137             var record = new Record(values, id);
15138             record.json = n;
15139             records[i] = record;
15140         }
15141         return {
15142             raw : o,
15143             success : success,
15144             records : records,
15145             totalRecords : totalRecords
15146         };
15147     },
15148     // used when loading children.. @see loadDataFromChildren
15149     toLoadData: function(rec)
15150     {
15151         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15152         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15153         return { data : data, total : data.length };
15154         
15155     }
15156 });/*
15157  * Based on:
15158  * Ext JS Library 1.1.1
15159  * Copyright(c) 2006-2007, Ext JS, LLC.
15160  *
15161  * Originally Released Under LGPL - original licence link has changed is not relivant.
15162  *
15163  * Fork - LGPL
15164  * <script type="text/javascript">
15165  */
15166
15167 /**
15168  * @class Roo.data.ArrayReader
15169  * @extends Roo.data.DataReader
15170  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15171  * Each element of that Array represents a row of data fields. The
15172  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15173  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15174  * <p>
15175  * Example code:.
15176  * <pre><code>
15177 var RecordDef = Roo.data.Record.create([
15178     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15179     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15180 ]);
15181 var myReader = new Roo.data.ArrayReader({
15182     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15183 }, RecordDef);
15184 </code></pre>
15185  * <p>
15186  * This would consume an Array like this:
15187  * <pre><code>
15188 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15189   </code></pre>
15190  
15191  * @constructor
15192  * Create a new JsonReader
15193  * @param {Object} meta Metadata configuration options.
15194  * @param {Object|Array} recordType Either an Array of field definition objects
15195  * 
15196  * @cfg {Array} fields Array of field definition objects
15197  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15198  * as specified to {@link Roo.data.Record#create},
15199  * or an {@link Roo.data.Record} object
15200  *
15201  * 
15202  * created using {@link Roo.data.Record#create}.
15203  */
15204 Roo.data.ArrayReader = function(meta, recordType)
15205 {    
15206     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15207 };
15208
15209 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15210     
15211       /**
15212      * Create a data block containing Roo.data.Records from an XML document.
15213      * @param {Object} o An Array of row objects which represents the dataset.
15214      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15215      * a cache of Roo.data.Records.
15216      */
15217     readRecords : function(o)
15218     {
15219         var sid = this.meta ? this.meta.id : null;
15220         var recordType = this.recordType, fields = recordType.prototype.fields;
15221         var records = [];
15222         var root = o;
15223         for(var i = 0; i < root.length; i++){
15224             var n = root[i];
15225             var values = {};
15226             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15227             for(var j = 0, jlen = fields.length; j < jlen; j++){
15228                 var f = fields.items[j];
15229                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15230                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15231                 v = f.convert(v);
15232                 values[f.name] = v;
15233             }
15234             var record = new recordType(values, id);
15235             record.json = n;
15236             records[records.length] = record;
15237         }
15238         return {
15239             records : records,
15240             totalRecords : records.length
15241         };
15242     },
15243     // used when loading children.. @see loadDataFromChildren
15244     toLoadData: function(rec)
15245     {
15246         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15247         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15248         
15249     }
15250     
15251     
15252 });/*
15253  * - LGPL
15254  * * 
15255  */
15256
15257 /**
15258  * @class Roo.bootstrap.ComboBox
15259  * @extends Roo.bootstrap.TriggerField
15260  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15261  * @cfg {Boolean} append (true|false) default false
15262  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15263  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15264  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15265  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15266  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15267  * @cfg {Boolean} animate default true
15268  * @cfg {Boolean} emptyResultText only for touch device
15269  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15270  * @cfg {String} emptyTitle default ''
15271  * @cfg {Number} width fixed with? experimental
15272  * @constructor
15273  * Create a new ComboBox.
15274  * @param {Object} config Configuration options
15275  */
15276 Roo.bootstrap.ComboBox = function(config){
15277     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15278     this.addEvents({
15279         /**
15280          * @event expand
15281          * Fires when the dropdown list is expanded
15282         * @param {Roo.bootstrap.ComboBox} combo This combo box
15283         */
15284         'expand' : true,
15285         /**
15286          * @event collapse
15287          * Fires when the dropdown list is collapsed
15288         * @param {Roo.bootstrap.ComboBox} combo This combo box
15289         */
15290         'collapse' : true,
15291         /**
15292          * @event beforeselect
15293          * Fires before a list item is selected. Return false to cancel the selection.
15294         * @param {Roo.bootstrap.ComboBox} combo This combo box
15295         * @param {Roo.data.Record} record The data record returned from the underlying store
15296         * @param {Number} index The index of the selected item in the dropdown list
15297         */
15298         'beforeselect' : true,
15299         /**
15300          * @event select
15301          * Fires when a list item is selected
15302         * @param {Roo.bootstrap.ComboBox} combo This combo box
15303         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15304         * @param {Number} index The index of the selected item in the dropdown list
15305         */
15306         'select' : true,
15307         /**
15308          * @event beforequery
15309          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15310          * The event object passed has these properties:
15311         * @param {Roo.bootstrap.ComboBox} combo This combo box
15312         * @param {String} query The query
15313         * @param {Boolean} forceAll true to force "all" query
15314         * @param {Boolean} cancel true to cancel the query
15315         * @param {Object} e The query event object
15316         */
15317         'beforequery': true,
15318          /**
15319          * @event add
15320          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15321         * @param {Roo.bootstrap.ComboBox} combo This combo box
15322         */
15323         'add' : true,
15324         /**
15325          * @event edit
15326          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15327         * @param {Roo.bootstrap.ComboBox} combo This combo box
15328         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15329         */
15330         'edit' : true,
15331         /**
15332          * @event remove
15333          * Fires when the remove value from the combobox array
15334         * @param {Roo.bootstrap.ComboBox} combo This combo box
15335         */
15336         'remove' : true,
15337         /**
15338          * @event afterremove
15339          * Fires when the remove value from the combobox array
15340         * @param {Roo.bootstrap.ComboBox} combo This combo box
15341         */
15342         'afterremove' : true,
15343         /**
15344          * @event specialfilter
15345          * Fires when specialfilter
15346             * @param {Roo.bootstrap.ComboBox} combo This combo box
15347             */
15348         'specialfilter' : true,
15349         /**
15350          * @event tick
15351          * Fires when tick the element
15352             * @param {Roo.bootstrap.ComboBox} combo This combo box
15353             */
15354         'tick' : true,
15355         /**
15356          * @event touchviewdisplay
15357          * Fires when touch view require special display (default is using displayField)
15358             * @param {Roo.bootstrap.ComboBox} combo This combo box
15359             * @param {Object} cfg set html .
15360             */
15361         'touchviewdisplay' : true
15362         
15363     });
15364     
15365     this.item = [];
15366     this.tickItems = [];
15367     
15368     this.selectedIndex = -1;
15369     if(this.mode == 'local'){
15370         if(config.queryDelay === undefined){
15371             this.queryDelay = 10;
15372         }
15373         if(config.minChars === undefined){
15374             this.minChars = 0;
15375         }
15376     }
15377 };
15378
15379 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15380      
15381     /**
15382      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15383      * rendering into an Roo.Editor, defaults to false)
15384      */
15385     /**
15386      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15387      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15388      */
15389     /**
15390      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15391      */
15392     /**
15393      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15394      * the dropdown list (defaults to undefined, with no header element)
15395      */
15396
15397      /**
15398      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15399      */
15400      
15401      /**
15402      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15403      */
15404     listWidth: undefined,
15405     /**
15406      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15407      * mode = 'remote' or 'text' if mode = 'local')
15408      */
15409     displayField: undefined,
15410     
15411     /**
15412      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15413      * mode = 'remote' or 'value' if mode = 'local'). 
15414      * Note: use of a valueField requires the user make a selection
15415      * in order for a value to be mapped.
15416      */
15417     valueField: undefined,
15418     /**
15419      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15420      */
15421     modalTitle : '',
15422     
15423     /**
15424      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15425      * field's data value (defaults to the underlying DOM element's name)
15426      */
15427     hiddenName: undefined,
15428     /**
15429      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15430      */
15431     listClass: '',
15432     /**
15433      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15434      */
15435     selectedClass: 'active',
15436     
15437     /**
15438      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15439      */
15440     shadow:'sides',
15441     /**
15442      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15443      * anchor positions (defaults to 'tl-bl')
15444      */
15445     listAlign: 'tl-bl?',
15446     /**
15447      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15448      */
15449     maxHeight: 300,
15450     /**
15451      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15452      * query specified by the allQuery config option (defaults to 'query')
15453      */
15454     triggerAction: 'query',
15455     /**
15456      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15457      * (defaults to 4, does not apply if editable = false)
15458      */
15459     minChars : 4,
15460     /**
15461      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15462      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15463      */
15464     typeAhead: false,
15465     /**
15466      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15467      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15468      */
15469     queryDelay: 500,
15470     /**
15471      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15472      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15473      */
15474     pageSize: 0,
15475     /**
15476      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15477      * when editable = true (defaults to false)
15478      */
15479     selectOnFocus:false,
15480     /**
15481      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15482      */
15483     queryParam: 'query',
15484     /**
15485      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15486      * when mode = 'remote' (defaults to 'Loading...')
15487      */
15488     loadingText: 'Loading...',
15489     /**
15490      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15491      */
15492     resizable: false,
15493     /**
15494      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15495      */
15496     handleHeight : 8,
15497     /**
15498      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15499      * traditional select (defaults to true)
15500      */
15501     editable: true,
15502     /**
15503      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15504      */
15505     allQuery: '',
15506     /**
15507      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15508      */
15509     mode: 'remote',
15510     /**
15511      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15512      * listWidth has a higher value)
15513      */
15514     minListWidth : 70,
15515     /**
15516      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15517      * allow the user to set arbitrary text into the field (defaults to false)
15518      */
15519     forceSelection:false,
15520     /**
15521      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15522      * if typeAhead = true (defaults to 250)
15523      */
15524     typeAheadDelay : 250,
15525     /**
15526      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15527      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15528      */
15529     valueNotFoundText : undefined,
15530     /**
15531      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15532      */
15533     blockFocus : false,
15534     
15535     /**
15536      * @cfg {Boolean} disableClear Disable showing of clear button.
15537      */
15538     disableClear : false,
15539     /**
15540      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15541      */
15542     alwaysQuery : false,
15543     
15544     /**
15545      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15546      */
15547     multiple : false,
15548     
15549     /**
15550      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15551      */
15552     invalidClass : "has-warning",
15553     
15554     /**
15555      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15556      */
15557     validClass : "has-success",
15558     
15559     /**
15560      * @cfg {Boolean} specialFilter (true|false) special filter default false
15561      */
15562     specialFilter : false,
15563     
15564     /**
15565      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15566      */
15567     mobileTouchView : true,
15568     
15569     /**
15570      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15571      */
15572     useNativeIOS : false,
15573     
15574     /**
15575      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15576      */
15577     mobile_restrict_height : false,
15578     
15579     ios_options : false,
15580     
15581     //private
15582     addicon : false,
15583     editicon: false,
15584     
15585     page: 0,
15586     hasQuery: false,
15587     append: false,
15588     loadNext: false,
15589     autoFocus : true,
15590     tickable : false,
15591     btnPosition : 'right',
15592     triggerList : true,
15593     showToggleBtn : true,
15594     animate : true,
15595     emptyResultText: 'Empty',
15596     triggerText : 'Select',
15597     emptyTitle : '',
15598     width : false,
15599     
15600     // element that contains real text value.. (when hidden is used..)
15601     
15602     getAutoCreate : function()
15603     {   
15604         var cfg = false;
15605         //render
15606         /*
15607          * Render classic select for iso
15608          */
15609         
15610         if(Roo.isIOS && this.useNativeIOS){
15611             cfg = this.getAutoCreateNativeIOS();
15612             return cfg;
15613         }
15614         
15615         /*
15616          * Touch Devices
15617          */
15618         
15619         if(Roo.isTouch && this.mobileTouchView){
15620             cfg = this.getAutoCreateTouchView();
15621             return cfg;;
15622         }
15623         
15624         /*
15625          *  Normal ComboBox
15626          */
15627         if(!this.tickable){
15628             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15629             return cfg;
15630         }
15631         
15632         /*
15633          *  ComboBox with tickable selections
15634          */
15635              
15636         var align = this.labelAlign || this.parentLabelAlign();
15637         
15638         cfg = {
15639             cls : 'form-group roo-combobox-tickable' //input-group
15640         };
15641         
15642         var btn_text_select = '';
15643         var btn_text_done = '';
15644         var btn_text_cancel = '';
15645         
15646         if (this.btn_text_show) {
15647             btn_text_select = 'Select';
15648             btn_text_done = 'Done';
15649             btn_text_cancel = 'Cancel'; 
15650         }
15651         
15652         var buttons = {
15653             tag : 'div',
15654             cls : 'tickable-buttons',
15655             cn : [
15656                 {
15657                     tag : 'button',
15658                     type : 'button',
15659                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15660                     //html : this.triggerText
15661                     html: btn_text_select
15662                 },
15663                 {
15664                     tag : 'button',
15665                     type : 'button',
15666                     name : 'ok',
15667                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15668                     //html : 'Done'
15669                     html: btn_text_done
15670                 },
15671                 {
15672                     tag : 'button',
15673                     type : 'button',
15674                     name : 'cancel',
15675                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15676                     //html : 'Cancel'
15677                     html: btn_text_cancel
15678                 }
15679             ]
15680         };
15681         
15682         if(this.editable){
15683             buttons.cn.unshift({
15684                 tag: 'input',
15685                 cls: 'roo-select2-search-field-input'
15686             });
15687         }
15688         
15689         var _this = this;
15690         
15691         Roo.each(buttons.cn, function(c){
15692             if (_this.size) {
15693                 c.cls += ' btn-' + _this.size;
15694             }
15695
15696             if (_this.disabled) {
15697                 c.disabled = true;
15698             }
15699         });
15700         
15701         var box = {
15702             tag: 'div',
15703             style : 'display: contents',
15704             cn: [
15705                 {
15706                     tag: 'input',
15707                     type : 'hidden',
15708                     cls: 'form-hidden-field'
15709                 },
15710                 {
15711                     tag: 'ul',
15712                     cls: 'roo-select2-choices',
15713                     cn:[
15714                         {
15715                             tag: 'li',
15716                             cls: 'roo-select2-search-field',
15717                             cn: [
15718                                 buttons
15719                             ]
15720                         }
15721                     ]
15722                 }
15723             ]
15724         };
15725         
15726         var combobox = {
15727             cls: 'roo-select2-container input-group roo-select2-container-multi',
15728             cn: [
15729                 
15730                 box
15731 //                {
15732 //                    tag: 'ul',
15733 //                    cls: 'typeahead typeahead-long dropdown-menu',
15734 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15735 //                }
15736             ]
15737         };
15738         
15739         if(this.hasFeedback && !this.allowBlank){
15740             
15741             var feedback = {
15742                 tag: 'span',
15743                 cls: 'glyphicon form-control-feedback'
15744             };
15745
15746             combobox.cn.push(feedback);
15747         }
15748         
15749         
15750         
15751         var indicator = {
15752             tag : 'i',
15753             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15754             tooltip : 'This field is required'
15755         };
15756         if (Roo.bootstrap.version == 4) {
15757             indicator = {
15758                 tag : 'i',
15759                 style : 'display:none'
15760             };
15761         }
15762         if (align ==='left' && this.fieldLabel.length) {
15763             
15764             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15765             
15766             cfg.cn = [
15767                 indicator,
15768                 {
15769                     tag: 'label',
15770                     'for' :  id,
15771                     cls : 'control-label col-form-label',
15772                     html : this.fieldLabel
15773
15774                 },
15775                 {
15776                     cls : "", 
15777                     cn: [
15778                         combobox
15779                     ]
15780                 }
15781
15782             ];
15783             
15784             var labelCfg = cfg.cn[1];
15785             var contentCfg = cfg.cn[2];
15786             
15787
15788             if(this.indicatorpos == 'right'){
15789                 
15790                 cfg.cn = [
15791                     {
15792                         tag: 'label',
15793                         'for' :  id,
15794                         cls : 'control-label col-form-label',
15795                         cn : [
15796                             {
15797                                 tag : 'span',
15798                                 html : this.fieldLabel
15799                             },
15800                             indicator
15801                         ]
15802                     },
15803                     {
15804                         cls : "",
15805                         cn: [
15806                             combobox
15807                         ]
15808                     }
15809
15810                 ];
15811                 
15812                 
15813                 
15814                 labelCfg = cfg.cn[0];
15815                 contentCfg = cfg.cn[1];
15816             
15817             }
15818             
15819             if(this.labelWidth > 12){
15820                 labelCfg.style = "width: " + this.labelWidth + 'px';
15821             }
15822             if(this.width * 1 > 0){
15823                 contentCfg.style = "width: " + this.width + 'px';
15824             }
15825             if(this.labelWidth < 13 && this.labelmd == 0){
15826                 this.labelmd = this.labelWidth;
15827             }
15828             
15829             if(this.labellg > 0){
15830                 labelCfg.cls += ' col-lg-' + this.labellg;
15831                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15832             }
15833             
15834             if(this.labelmd > 0){
15835                 labelCfg.cls += ' col-md-' + this.labelmd;
15836                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15837             }
15838             
15839             if(this.labelsm > 0){
15840                 labelCfg.cls += ' col-sm-' + this.labelsm;
15841                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15842             }
15843             
15844             if(this.labelxs > 0){
15845                 labelCfg.cls += ' col-xs-' + this.labelxs;
15846                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15847             }
15848                 
15849                 
15850         } else if ( this.fieldLabel.length) {
15851 //                Roo.log(" label");
15852                  cfg.cn = [
15853                    indicator,
15854                     {
15855                         tag: 'label',
15856                         //cls : 'input-group-addon',
15857                         html : this.fieldLabel
15858                     },
15859                     combobox
15860                 ];
15861                 
15862                 if(this.indicatorpos == 'right'){
15863                     cfg.cn = [
15864                         {
15865                             tag: 'label',
15866                             //cls : 'input-group-addon',
15867                             html : this.fieldLabel
15868                         },
15869                         indicator,
15870                         combobox
15871                     ];
15872                     
15873                 }
15874
15875         } else {
15876             
15877 //                Roo.log(" no label && no align");
15878                 cfg = combobox
15879                      
15880                 
15881         }
15882          
15883         var settings=this;
15884         ['xs','sm','md','lg'].map(function(size){
15885             if (settings[size]) {
15886                 cfg.cls += ' col-' + size + '-' + settings[size];
15887             }
15888         });
15889         
15890         return cfg;
15891         
15892     },
15893     
15894     _initEventsCalled : false,
15895     
15896     // private
15897     initEvents: function()
15898     {   
15899         if (this._initEventsCalled) { // as we call render... prevent looping...
15900             return;
15901         }
15902         this._initEventsCalled = true;
15903         
15904         if (!this.store) {
15905             throw "can not find store for combo";
15906         }
15907         
15908         this.indicator = this.indicatorEl();
15909         
15910         this.store = Roo.factory(this.store, Roo.data);
15911         this.store.parent = this;
15912         
15913         // if we are building from html. then this element is so complex, that we can not really
15914         // use the rendered HTML.
15915         // so we have to trash and replace the previous code.
15916         if (Roo.XComponent.build_from_html) {
15917             // remove this element....
15918             var e = this.el.dom, k=0;
15919             while (e ) { e = e.previousSibling;  ++k;}
15920
15921             this.el.remove();
15922             
15923             this.el=false;
15924             this.rendered = false;
15925             
15926             this.render(this.parent().getChildContainer(true), k);
15927         }
15928         
15929         if(Roo.isIOS && this.useNativeIOS){
15930             this.initIOSView();
15931             return;
15932         }
15933         
15934         /*
15935          * Touch Devices
15936          */
15937         
15938         if(Roo.isTouch && this.mobileTouchView){
15939             this.initTouchView();
15940             return;
15941         }
15942         
15943         if(this.tickable){
15944             this.initTickableEvents();
15945             return;
15946         }
15947         
15948         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15949         
15950         if(this.hiddenName){
15951             
15952             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15953             
15954             this.hiddenField.dom.value =
15955                 this.hiddenValue !== undefined ? this.hiddenValue :
15956                 this.value !== undefined ? this.value : '';
15957
15958             // prevent input submission
15959             this.el.dom.removeAttribute('name');
15960             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15961              
15962              
15963         }
15964         //if(Roo.isGecko){
15965         //    this.el.dom.setAttribute('autocomplete', 'off');
15966         //}
15967         
15968         var cls = 'x-combo-list';
15969         
15970         //this.list = new Roo.Layer({
15971         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15972         //});
15973         
15974         var _this = this;
15975         
15976         (function(){
15977             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15978             _this.list.setWidth(lw);
15979         }).defer(100);
15980         
15981         this.list.on('mouseover', this.onViewOver, this);
15982         this.list.on('mousemove', this.onViewMove, this);
15983         this.list.on('scroll', this.onViewScroll, this);
15984         
15985         /*
15986         this.list.swallowEvent('mousewheel');
15987         this.assetHeight = 0;
15988
15989         if(this.title){
15990             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15991             this.assetHeight += this.header.getHeight();
15992         }
15993
15994         this.innerList = this.list.createChild({cls:cls+'-inner'});
15995         this.innerList.on('mouseover', this.onViewOver, this);
15996         this.innerList.on('mousemove', this.onViewMove, this);
15997         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998         
15999         if(this.allowBlank && !this.pageSize && !this.disableClear){
16000             this.footer = this.list.createChild({cls:cls+'-ft'});
16001             this.pageTb = new Roo.Toolbar(this.footer);
16002            
16003         }
16004         if(this.pageSize){
16005             this.footer = this.list.createChild({cls:cls+'-ft'});
16006             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16007                     {pageSize: this.pageSize});
16008             
16009         }
16010         
16011         if (this.pageTb && this.allowBlank && !this.disableClear) {
16012             var _this = this;
16013             this.pageTb.add(new Roo.Toolbar.Fill(), {
16014                 cls: 'x-btn-icon x-btn-clear',
16015                 text: '&#160;',
16016                 handler: function()
16017                 {
16018                     _this.collapse();
16019                     _this.clearValue();
16020                     _this.onSelect(false, -1);
16021                 }
16022             });
16023         }
16024         if (this.footer) {
16025             this.assetHeight += this.footer.getHeight();
16026         }
16027         */
16028             
16029         if(!this.tpl){
16030             this.tpl = Roo.bootstrap.version == 4 ?
16031                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16032                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16033         }
16034
16035         this.view = new Roo.View(this.list, this.tpl, {
16036             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16037         });
16038         //this.view.wrapEl.setDisplayed(false);
16039         this.view.on('click', this.onViewClick, this);
16040         
16041         
16042         this.store.on('beforeload', this.onBeforeLoad, this);
16043         this.store.on('load', this.onLoad, this);
16044         this.store.on('loadexception', this.onLoadException, this);
16045         /*
16046         if(this.resizable){
16047             this.resizer = new Roo.Resizable(this.list,  {
16048                pinned:true, handles:'se'
16049             });
16050             this.resizer.on('resize', function(r, w, h){
16051                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16052                 this.listWidth = w;
16053                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16054                 this.restrictHeight();
16055             }, this);
16056             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16057         }
16058         */
16059         if(!this.editable){
16060             this.editable = true;
16061             this.setEditable(false);
16062         }
16063         
16064         /*
16065         
16066         if (typeof(this.events.add.listeners) != 'undefined') {
16067             
16068             this.addicon = this.wrap.createChild(
16069                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16070        
16071             this.addicon.on('click', function(e) {
16072                 this.fireEvent('add', this);
16073             }, this);
16074         }
16075         if (typeof(this.events.edit.listeners) != 'undefined') {
16076             
16077             this.editicon = this.wrap.createChild(
16078                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16079             if (this.addicon) {
16080                 this.editicon.setStyle('margin-left', '40px');
16081             }
16082             this.editicon.on('click', function(e) {
16083                 
16084                 // we fire even  if inothing is selected..
16085                 this.fireEvent('edit', this, this.lastData );
16086                 
16087             }, this);
16088         }
16089         */
16090         
16091         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16092             "up" : function(e){
16093                 this.inKeyMode = true;
16094                 this.selectPrev();
16095             },
16096
16097             "down" : function(e){
16098                 if(!this.isExpanded()){
16099                     this.onTriggerClick();
16100                 }else{
16101                     this.inKeyMode = true;
16102                     this.selectNext();
16103                 }
16104             },
16105
16106             "enter" : function(e){
16107 //                this.onViewClick();
16108                 //return true;
16109                 this.collapse();
16110                 
16111                 if(this.fireEvent("specialkey", this, e)){
16112                     this.onViewClick(false);
16113                 }
16114                 
16115                 return true;
16116             },
16117
16118             "esc" : function(e){
16119                 this.collapse();
16120             },
16121
16122             "tab" : function(e){
16123                 this.collapse();
16124                 
16125                 if(this.fireEvent("specialkey", this, e)){
16126                     this.onViewClick(false);
16127                 }
16128                 
16129                 return true;
16130             },
16131
16132             scope : this,
16133
16134             doRelay : function(foo, bar, hname){
16135                 if(hname == 'down' || this.scope.isExpanded()){
16136                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16137                 }
16138                 return true;
16139             },
16140
16141             forceKeyDown: true
16142         });
16143         
16144         
16145         this.queryDelay = Math.max(this.queryDelay || 10,
16146                 this.mode == 'local' ? 10 : 250);
16147         
16148         
16149         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16150         
16151         if(this.typeAhead){
16152             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16153         }
16154         if(this.editable !== false){
16155             this.inputEl().on("keyup", this.onKeyUp, this);
16156         }
16157         if(this.forceSelection){
16158             this.inputEl().on('blur', this.doForce, this);
16159         }
16160         
16161         if(this.multiple){
16162             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16163             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16164         }
16165     },
16166     
16167     initTickableEvents: function()
16168     {   
16169         this.createList();
16170         
16171         if(this.hiddenName){
16172             
16173             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16174             
16175             this.hiddenField.dom.value =
16176                 this.hiddenValue !== undefined ? this.hiddenValue :
16177                 this.value !== undefined ? this.value : '';
16178
16179             // prevent input submission
16180             this.el.dom.removeAttribute('name');
16181             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16182              
16183              
16184         }
16185         
16186 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16187         
16188         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16189         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16190         if(this.triggerList){
16191             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16192         }
16193          
16194         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16195         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16196         
16197         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16198         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16199         
16200         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16201         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16202         
16203         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16204         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16205         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16206         
16207         this.okBtn.hide();
16208         this.cancelBtn.hide();
16209         
16210         var _this = this;
16211         
16212         (function(){
16213             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16214             _this.list.setWidth(lw);
16215         }).defer(100);
16216         
16217         this.list.on('mouseover', this.onViewOver, this);
16218         this.list.on('mousemove', this.onViewMove, this);
16219         
16220         this.list.on('scroll', this.onViewScroll, this);
16221         
16222         if(!this.tpl){
16223             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16224                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16225         }
16226
16227         this.view = new Roo.View(this.list, this.tpl, {
16228             singleSelect:true,
16229             tickable:true,
16230             parent:this,
16231             store: this.store,
16232             selectedClass: this.selectedClass
16233         });
16234         
16235         //this.view.wrapEl.setDisplayed(false);
16236         this.view.on('click', this.onViewClick, this);
16237         
16238         
16239         
16240         this.store.on('beforeload', this.onBeforeLoad, this);
16241         this.store.on('load', this.onLoad, this);
16242         this.store.on('loadexception', this.onLoadException, this);
16243         
16244         if(this.editable){
16245             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16246                 "up" : function(e){
16247                     this.inKeyMode = true;
16248                     this.selectPrev();
16249                 },
16250
16251                 "down" : function(e){
16252                     this.inKeyMode = true;
16253                     this.selectNext();
16254                 },
16255
16256                 "enter" : function(e){
16257                     if(this.fireEvent("specialkey", this, e)){
16258                         this.onViewClick(false);
16259                     }
16260                     
16261                     return true;
16262                 },
16263
16264                 "esc" : function(e){
16265                     this.onTickableFooterButtonClick(e, false, false);
16266                 },
16267
16268                 "tab" : function(e){
16269                     this.fireEvent("specialkey", this, e);
16270                     
16271                     this.onTickableFooterButtonClick(e, false, false);
16272                     
16273                     return true;
16274                 },
16275
16276                 scope : this,
16277
16278                 doRelay : function(e, fn, key){
16279                     if(this.scope.isExpanded()){
16280                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16281                     }
16282                     return true;
16283                 },
16284
16285                 forceKeyDown: true
16286             });
16287         }
16288         
16289         this.queryDelay = Math.max(this.queryDelay || 10,
16290                 this.mode == 'local' ? 10 : 250);
16291         
16292         
16293         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16294         
16295         if(this.typeAhead){
16296             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16297         }
16298         
16299         if(this.editable !== false){
16300             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16301         }
16302         
16303         this.indicator = this.indicatorEl();
16304         
16305         if(this.indicator){
16306             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16307             this.indicator.hide();
16308         }
16309         
16310     },
16311
16312     onDestroy : function(){
16313         if(this.view){
16314             this.view.setStore(null);
16315             this.view.el.removeAllListeners();
16316             this.view.el.remove();
16317             this.view.purgeListeners();
16318         }
16319         if(this.list){
16320             this.list.dom.innerHTML  = '';
16321         }
16322         
16323         if(this.store){
16324             this.store.un('beforeload', this.onBeforeLoad, this);
16325             this.store.un('load', this.onLoad, this);
16326             this.store.un('loadexception', this.onLoadException, this);
16327         }
16328         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16329     },
16330
16331     // private
16332     fireKey : function(e){
16333         if(e.isNavKeyPress() && !this.list.isVisible()){
16334             this.fireEvent("specialkey", this, e);
16335         }
16336     },
16337
16338     // private
16339     onResize: function(w, h)
16340     {
16341         
16342         
16343 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16344 //        
16345 //        if(typeof w != 'number'){
16346 //            // we do not handle it!?!?
16347 //            return;
16348 //        }
16349 //        var tw = this.trigger.getWidth();
16350 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16351 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16352 //        var x = w - tw;
16353 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16354 //            
16355 //        //this.trigger.setStyle('left', x+'px');
16356 //        
16357 //        if(this.list && this.listWidth === undefined){
16358 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16359 //            this.list.setWidth(lw);
16360 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16361 //        }
16362         
16363     
16364         
16365     },
16366
16367     /**
16368      * Allow or prevent the user from directly editing the field text.  If false is passed,
16369      * the user will only be able to select from the items defined in the dropdown list.  This method
16370      * is the runtime equivalent of setting the 'editable' config option at config time.
16371      * @param {Boolean} value True to allow the user to directly edit the field text
16372      */
16373     setEditable : function(value){
16374         if(value == this.editable){
16375             return;
16376         }
16377         this.editable = value;
16378         if(!value){
16379             this.inputEl().dom.setAttribute('readOnly', true);
16380             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16381             this.inputEl().addClass('x-combo-noedit');
16382         }else{
16383             this.inputEl().dom.removeAttribute('readOnly');
16384             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16385             this.inputEl().removeClass('x-combo-noedit');
16386         }
16387     },
16388
16389     // private
16390     
16391     onBeforeLoad : function(combo,opts){
16392         if(!this.hasFocus){
16393             return;
16394         }
16395          if (!opts.add) {
16396             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16397          }
16398         this.restrictHeight();
16399         this.selectedIndex = -1;
16400     },
16401
16402     // private
16403     onLoad : function(){
16404         
16405         this.hasQuery = false;
16406         
16407         if(!this.hasFocus){
16408             return;
16409         }
16410         
16411         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16412             this.loading.hide();
16413         }
16414         
16415         if(this.store.getCount() > 0){
16416             
16417             this.expand();
16418             this.restrictHeight();
16419             if(this.lastQuery == this.allQuery){
16420                 if(this.editable && !this.tickable){
16421                     this.inputEl().dom.select();
16422                 }
16423                 
16424                 if(
16425                     !this.selectByValue(this.value, true) &&
16426                     this.autoFocus && 
16427                     (
16428                         !this.store.lastOptions ||
16429                         typeof(this.store.lastOptions.add) == 'undefined' || 
16430                         this.store.lastOptions.add != true
16431                     )
16432                 ){
16433                     this.select(0, true);
16434                 }
16435             }else{
16436                 if(this.autoFocus){
16437                     this.selectNext();
16438                 }
16439                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16440                     this.taTask.delay(this.typeAheadDelay);
16441                 }
16442             }
16443         }else{
16444             this.onEmptyResults();
16445         }
16446         
16447         //this.el.focus();
16448     },
16449     // private
16450     onLoadException : function()
16451     {
16452         this.hasQuery = false;
16453         
16454         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16455             this.loading.hide();
16456         }
16457         
16458         if(this.tickable && this.editable){
16459             return;
16460         }
16461         
16462         this.collapse();
16463         // only causes errors at present
16464         //Roo.log(this.store.reader.jsonData);
16465         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16466             // fixme
16467             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16468         //}
16469         
16470         
16471     },
16472     // private
16473     onTypeAhead : function(){
16474         if(this.store.getCount() > 0){
16475             var r = this.store.getAt(0);
16476             var newValue = r.data[this.displayField];
16477             var len = newValue.length;
16478             var selStart = this.getRawValue().length;
16479             
16480             if(selStart != len){
16481                 this.setRawValue(newValue);
16482                 this.selectText(selStart, newValue.length);
16483             }
16484         }
16485     },
16486
16487     // private
16488     onSelect : function(record, index){
16489         
16490         if(this.fireEvent('beforeselect', this, record, index) !== false){
16491         
16492             this.setFromData(index > -1 ? record.data : false);
16493             
16494             this.collapse();
16495             this.fireEvent('select', this, record, index);
16496         }
16497     },
16498
16499     /**
16500      * Returns the currently selected field value or empty string if no value is set.
16501      * @return {String} value The selected value
16502      */
16503     getValue : function()
16504     {
16505         if(Roo.isIOS && this.useNativeIOS){
16506             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16507         }
16508         
16509         if(this.multiple){
16510             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16511         }
16512         
16513         if(this.valueField){
16514             return typeof this.value != 'undefined' ? this.value : '';
16515         }else{
16516             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16517         }
16518     },
16519     
16520     getRawValue : function()
16521     {
16522         if(Roo.isIOS && this.useNativeIOS){
16523             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16524         }
16525         
16526         var v = this.inputEl().getValue();
16527         
16528         return v;
16529     },
16530
16531     /**
16532      * Clears any text/value currently set in the field
16533      */
16534     clearValue : function(){
16535         
16536         if(this.hiddenField){
16537             this.hiddenField.dom.value = '';
16538         }
16539         this.value = '';
16540         this.setRawValue('');
16541         this.lastSelectionText = '';
16542         this.lastData = false;
16543         
16544         var close = this.closeTriggerEl();
16545         
16546         if(close){
16547             close.hide();
16548         }
16549         
16550         this.validate();
16551         
16552     },
16553
16554     /**
16555      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16556      * will be displayed in the field.  If the value does not match the data value of an existing item,
16557      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16558      * Otherwise the field will be blank (although the value will still be set).
16559      * @param {String} value The value to match
16560      */
16561     setValue : function(v)
16562     {
16563         if(Roo.isIOS && this.useNativeIOS){
16564             this.setIOSValue(v);
16565             return;
16566         }
16567         
16568         if(this.multiple){
16569             this.syncValue();
16570             return;
16571         }
16572         
16573         var text = v;
16574         if(this.valueField){
16575             var r = this.findRecord(this.valueField, v);
16576             if(r){
16577                 text = r.data[this.displayField];
16578             }else if(this.valueNotFoundText !== undefined){
16579                 text = this.valueNotFoundText;
16580             }
16581         }
16582         this.lastSelectionText = text;
16583         if(this.hiddenField){
16584             this.hiddenField.dom.value = v;
16585         }
16586         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16587         this.value = v;
16588         
16589         var close = this.closeTriggerEl();
16590         
16591         if(close){
16592             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16593         }
16594         
16595         this.validate();
16596     },
16597     /**
16598      * @property {Object} the last set data for the element
16599      */
16600     
16601     lastData : false,
16602     /**
16603      * Sets the value of the field based on a object which is related to the record format for the store.
16604      * @param {Object} value the value to set as. or false on reset?
16605      */
16606     setFromData : function(o){
16607         
16608         if(this.multiple){
16609             this.addItem(o);
16610             return;
16611         }
16612             
16613         var dv = ''; // display value
16614         var vv = ''; // value value..
16615         this.lastData = o;
16616         if (this.displayField) {
16617             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16618         } else {
16619             // this is an error condition!!!
16620             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16621         }
16622         
16623         if(this.valueField){
16624             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16625         }
16626         
16627         var close = this.closeTriggerEl();
16628         
16629         if(close){
16630             if(dv.length || vv * 1 > 0){
16631                 close.show() ;
16632                 this.blockFocus=true;
16633             } else {
16634                 close.hide();
16635             }             
16636         }
16637         
16638         if(this.hiddenField){
16639             this.hiddenField.dom.value = vv;
16640             
16641             this.lastSelectionText = dv;
16642             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16643             this.value = vv;
16644             return;
16645         }
16646         // no hidden field.. - we store the value in 'value', but still display
16647         // display field!!!!
16648         this.lastSelectionText = dv;
16649         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16650         this.value = vv;
16651         
16652         
16653         
16654     },
16655     // private
16656     reset : function(){
16657         // overridden so that last data is reset..
16658         
16659         if(this.multiple){
16660             this.clearItem();
16661             return;
16662         }
16663         
16664         this.setValue(this.originalValue);
16665         //this.clearInvalid();
16666         this.lastData = false;
16667         if (this.view) {
16668             this.view.clearSelections();
16669         }
16670         
16671         this.validate();
16672     },
16673     // private
16674     findRecord : function(prop, value){
16675         var record;
16676         if(this.store.getCount() > 0){
16677             this.store.each(function(r){
16678                 if(r.data[prop] == value){
16679                     record = r;
16680                     return false;
16681                 }
16682                 return true;
16683             });
16684         }
16685         return record;
16686     },
16687     
16688     getName: function()
16689     {
16690         // returns hidden if it's set..
16691         if (!this.rendered) {return ''};
16692         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16693         
16694     },
16695     // private
16696     onViewMove : function(e, t){
16697         this.inKeyMode = false;
16698     },
16699
16700     // private
16701     onViewOver : function(e, t){
16702         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16703             return;
16704         }
16705         var item = this.view.findItemFromChild(t);
16706         
16707         if(item){
16708             var index = this.view.indexOf(item);
16709             this.select(index, false);
16710         }
16711     },
16712
16713     // private
16714     onViewClick : function(view, doFocus, el, e)
16715     {
16716         var index = this.view.getSelectedIndexes()[0];
16717         
16718         var r = this.store.getAt(index);
16719         
16720         if(this.tickable){
16721             
16722             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16723                 return;
16724             }
16725             
16726             var rm = false;
16727             var _this = this;
16728             
16729             Roo.each(this.tickItems, function(v,k){
16730                 
16731                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16732                     Roo.log(v);
16733                     _this.tickItems.splice(k, 1);
16734                     
16735                     if(typeof(e) == 'undefined' && view == false){
16736                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16737                     }
16738                     
16739                     rm = true;
16740                     return;
16741                 }
16742             });
16743             
16744             if(rm){
16745                 return;
16746             }
16747             
16748             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16749                 this.tickItems.push(r.data);
16750             }
16751             
16752             if(typeof(e) == 'undefined' && view == false){
16753                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16754             }
16755                     
16756             return;
16757         }
16758         
16759         if(r){
16760             this.onSelect(r, index);
16761         }
16762         if(doFocus !== false && !this.blockFocus){
16763             this.inputEl().focus();
16764         }
16765     },
16766
16767     // private
16768     restrictHeight : function(){
16769         //this.innerList.dom.style.height = '';
16770         //var inner = this.innerList.dom;
16771         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16772         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16773         //this.list.beginUpdate();
16774         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16775         this.list.alignTo(this.inputEl(), this.listAlign);
16776         this.list.alignTo(this.inputEl(), this.listAlign);
16777         //this.list.endUpdate();
16778     },
16779
16780     // private
16781     onEmptyResults : function(){
16782         
16783         if(this.tickable && this.editable){
16784             this.hasFocus = false;
16785             this.restrictHeight();
16786             return;
16787         }
16788         
16789         this.collapse();
16790     },
16791
16792     /**
16793      * Returns true if the dropdown list is expanded, else false.
16794      */
16795     isExpanded : function(){
16796         return this.list.isVisible();
16797     },
16798
16799     /**
16800      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16801      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16802      * @param {String} value The data value of the item to select
16803      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16804      * selected item if it is not currently in view (defaults to true)
16805      * @return {Boolean} True if the value matched an item in the list, else false
16806      */
16807     selectByValue : function(v, scrollIntoView){
16808         if(v !== undefined && v !== null){
16809             var r = this.findRecord(this.valueField || this.displayField, v);
16810             if(r){
16811                 this.select(this.store.indexOf(r), scrollIntoView);
16812                 return true;
16813             }
16814         }
16815         return false;
16816     },
16817
16818     /**
16819      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16820      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16821      * @param {Number} index The zero-based index of the list item to select
16822      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16823      * selected item if it is not currently in view (defaults to true)
16824      */
16825     select : function(index, scrollIntoView){
16826         this.selectedIndex = index;
16827         this.view.select(index);
16828         if(scrollIntoView !== false){
16829             var el = this.view.getNode(index);
16830             /*
16831              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16832              */
16833             if(el){
16834                 this.list.scrollChildIntoView(el, false);
16835             }
16836         }
16837     },
16838
16839     // private
16840     selectNext : function(){
16841         var ct = this.store.getCount();
16842         if(ct > 0){
16843             if(this.selectedIndex == -1){
16844                 this.select(0);
16845             }else if(this.selectedIndex < ct-1){
16846                 this.select(this.selectedIndex+1);
16847             }
16848         }
16849     },
16850
16851     // private
16852     selectPrev : function(){
16853         var ct = this.store.getCount();
16854         if(ct > 0){
16855             if(this.selectedIndex == -1){
16856                 this.select(0);
16857             }else if(this.selectedIndex != 0){
16858                 this.select(this.selectedIndex-1);
16859             }
16860         }
16861     },
16862
16863     // private
16864     onKeyUp : function(e){
16865         if(this.editable !== false && !e.isSpecialKey()){
16866             this.lastKey = e.getKey();
16867             this.dqTask.delay(this.queryDelay);
16868         }
16869     },
16870
16871     // private
16872     validateBlur : function(){
16873         return !this.list || !this.list.isVisible();   
16874     },
16875
16876     // private
16877     initQuery : function(){
16878         
16879         var v = this.getRawValue();
16880         
16881         if(this.tickable && this.editable){
16882             v = this.tickableInputEl().getValue();
16883         }
16884         
16885         this.doQuery(v);
16886     },
16887
16888     // private
16889     doForce : function(){
16890         if(this.inputEl().dom.value.length > 0){
16891             this.inputEl().dom.value =
16892                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16893              
16894         }
16895     },
16896
16897     /**
16898      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16899      * query allowing the query action to be canceled if needed.
16900      * @param {String} query The SQL query to execute
16901      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16902      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16903      * saved in the current store (defaults to false)
16904      */
16905     doQuery : function(q, forceAll){
16906         
16907         if(q === undefined || q === null){
16908             q = '';
16909         }
16910         var qe = {
16911             query: q,
16912             forceAll: forceAll,
16913             combo: this,
16914             cancel:false
16915         };
16916         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16917             return false;
16918         }
16919         q = qe.query;
16920         
16921         forceAll = qe.forceAll;
16922         if(forceAll === true || (q.length >= this.minChars)){
16923             
16924             this.hasQuery = true;
16925             
16926             if(this.lastQuery != q || this.alwaysQuery){
16927                 this.lastQuery = q;
16928                 if(this.mode == 'local'){
16929                     this.selectedIndex = -1;
16930                     if(forceAll){
16931                         this.store.clearFilter();
16932                     }else{
16933                         
16934                         if(this.specialFilter){
16935                             this.fireEvent('specialfilter', this);
16936                             this.onLoad();
16937                             return;
16938                         }
16939                         
16940                         this.store.filter(this.displayField, q);
16941                     }
16942                     
16943                     this.store.fireEvent("datachanged", this.store);
16944                     
16945                     this.onLoad();
16946                     
16947                     
16948                 }else{
16949                     
16950                     this.store.baseParams[this.queryParam] = q;
16951                     
16952                     var options = {params : this.getParams(q)};
16953                     
16954                     if(this.loadNext){
16955                         options.add = true;
16956                         options.params.start = this.page * this.pageSize;
16957                     }
16958                     
16959                     this.store.load(options);
16960                     
16961                     /*
16962                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16963                      *  we should expand the list on onLoad
16964                      *  so command out it
16965                      */
16966 //                    this.expand();
16967                 }
16968             }else{
16969                 this.selectedIndex = -1;
16970                 this.onLoad();   
16971             }
16972         }
16973         
16974         this.loadNext = false;
16975     },
16976     
16977     // private
16978     getParams : function(q){
16979         var p = {};
16980         //p[this.queryParam] = q;
16981         
16982         if(this.pageSize){
16983             p.start = 0;
16984             p.limit = this.pageSize;
16985         }
16986         return p;
16987     },
16988
16989     /**
16990      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16991      */
16992     collapse : function(){
16993         if(!this.isExpanded()){
16994             return;
16995         }
16996         
16997         this.list.hide();
16998         
16999         this.hasFocus = false;
17000         
17001         if(this.tickable){
17002             this.okBtn.hide();
17003             this.cancelBtn.hide();
17004             this.trigger.show();
17005             
17006             if(this.editable){
17007                 this.tickableInputEl().dom.value = '';
17008                 this.tickableInputEl().blur();
17009             }
17010             
17011         }
17012         
17013         Roo.get(document).un('mousedown', this.collapseIf, this);
17014         Roo.get(document).un('mousewheel', this.collapseIf, this);
17015         if (!this.editable) {
17016             Roo.get(document).un('keydown', this.listKeyPress, this);
17017         }
17018         this.fireEvent('collapse', this);
17019         
17020         this.validate();
17021     },
17022
17023     // private
17024     collapseIf : function(e){
17025         var in_combo  = e.within(this.el);
17026         var in_list =  e.within(this.list);
17027         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17028         
17029         if (in_combo || in_list || is_list) {
17030             //e.stopPropagation();
17031             return;
17032         }
17033         
17034         if(this.tickable){
17035             this.onTickableFooterButtonClick(e, false, false);
17036         }
17037
17038         this.collapse();
17039         
17040     },
17041
17042     /**
17043      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17044      */
17045     expand : function(){
17046        
17047         if(this.isExpanded() || !this.hasFocus){
17048             return;
17049         }
17050         
17051         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17052         this.list.setWidth(lw);
17053         
17054         Roo.log('expand');
17055         
17056         this.list.show();
17057         
17058         this.restrictHeight();
17059         
17060         if(this.tickable){
17061             
17062             this.tickItems = Roo.apply([], this.item);
17063             
17064             this.okBtn.show();
17065             this.cancelBtn.show();
17066             this.trigger.hide();
17067             
17068             if(this.editable){
17069                 this.tickableInputEl().focus();
17070             }
17071             
17072         }
17073         
17074         Roo.get(document).on('mousedown', this.collapseIf, this);
17075         Roo.get(document).on('mousewheel', this.collapseIf, this);
17076         if (!this.editable) {
17077             Roo.get(document).on('keydown', this.listKeyPress, this);
17078         }
17079         
17080         this.fireEvent('expand', this);
17081     },
17082
17083     // private
17084     // Implements the default empty TriggerField.onTriggerClick function
17085     onTriggerClick : function(e)
17086     {
17087         Roo.log('trigger click');
17088         
17089         if(this.disabled || !this.triggerList){
17090             return;
17091         }
17092         
17093         this.page = 0;
17094         this.loadNext = false;
17095         
17096         if(this.isExpanded()){
17097             this.collapse();
17098             if (!this.blockFocus) {
17099                 this.inputEl().focus();
17100             }
17101             
17102         }else {
17103             this.hasFocus = true;
17104             if(this.triggerAction == 'all') {
17105                 this.doQuery(this.allQuery, true);
17106             } else {
17107                 this.doQuery(this.getRawValue());
17108             }
17109             if (!this.blockFocus) {
17110                 this.inputEl().focus();
17111             }
17112         }
17113     },
17114     
17115     onTickableTriggerClick : function(e)
17116     {
17117         if(this.disabled){
17118             return;
17119         }
17120         
17121         this.page = 0;
17122         this.loadNext = false;
17123         this.hasFocus = true;
17124         
17125         if(this.triggerAction == 'all') {
17126             this.doQuery(this.allQuery, true);
17127         } else {
17128             this.doQuery(this.getRawValue());
17129         }
17130     },
17131     
17132     onSearchFieldClick : function(e)
17133     {
17134         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17135             this.onTickableFooterButtonClick(e, false, false);
17136             return;
17137         }
17138         
17139         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17140             return;
17141         }
17142         
17143         this.page = 0;
17144         this.loadNext = false;
17145         this.hasFocus = true;
17146         
17147         if(this.triggerAction == 'all') {
17148             this.doQuery(this.allQuery, true);
17149         } else {
17150             this.doQuery(this.getRawValue());
17151         }
17152     },
17153     
17154     listKeyPress : function(e)
17155     {
17156         //Roo.log('listkeypress');
17157         // scroll to first matching element based on key pres..
17158         if (e.isSpecialKey()) {
17159             return false;
17160         }
17161         var k = String.fromCharCode(e.getKey()).toUpperCase();
17162         //Roo.log(k);
17163         var match  = false;
17164         var csel = this.view.getSelectedNodes();
17165         var cselitem = false;
17166         if (csel.length) {
17167             var ix = this.view.indexOf(csel[0]);
17168             cselitem  = this.store.getAt(ix);
17169             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17170                 cselitem = false;
17171             }
17172             
17173         }
17174         
17175         this.store.each(function(v) { 
17176             if (cselitem) {
17177                 // start at existing selection.
17178                 if (cselitem.id == v.id) {
17179                     cselitem = false;
17180                 }
17181                 return true;
17182             }
17183                 
17184             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17185                 match = this.store.indexOf(v);
17186                 return false;
17187             }
17188             return true;
17189         }, this);
17190         
17191         if (match === false) {
17192             return true; // no more action?
17193         }
17194         // scroll to?
17195         this.view.select(match);
17196         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17197         sn.scrollIntoView(sn.dom.parentNode, false);
17198     },
17199     
17200     onViewScroll : function(e, t){
17201         
17202         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){
17203             return;
17204         }
17205         
17206         this.hasQuery = true;
17207         
17208         this.loading = this.list.select('.loading', true).first();
17209         
17210         if(this.loading === null){
17211             this.list.createChild({
17212                 tag: 'div',
17213                 cls: 'loading roo-select2-more-results roo-select2-active',
17214                 html: 'Loading more results...'
17215             });
17216             
17217             this.loading = this.list.select('.loading', true).first();
17218             
17219             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17220             
17221             this.loading.hide();
17222         }
17223         
17224         this.loading.show();
17225         
17226         var _combo = this;
17227         
17228         this.page++;
17229         this.loadNext = true;
17230         
17231         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17232         
17233         return;
17234     },
17235     
17236     addItem : function(o)
17237     {   
17238         var dv = ''; // display value
17239         
17240         if (this.displayField) {
17241             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17242         } else {
17243             // this is an error condition!!!
17244             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17245         }
17246         
17247         if(!dv.length){
17248             return;
17249         }
17250         
17251         var choice = this.choices.createChild({
17252             tag: 'li',
17253             cls: 'roo-select2-search-choice',
17254             cn: [
17255                 {
17256                     tag: 'div',
17257                     html: dv
17258                 },
17259                 {
17260                     tag: 'a',
17261                     href: '#',
17262                     cls: 'roo-select2-search-choice-close fa fa-times',
17263                     tabindex: '-1'
17264                 }
17265             ]
17266             
17267         }, this.searchField);
17268         
17269         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17270         
17271         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17272         
17273         this.item.push(o);
17274         
17275         this.lastData = o;
17276         
17277         this.syncValue();
17278         
17279         this.inputEl().dom.value = '';
17280         
17281         this.validate();
17282     },
17283     
17284     onRemoveItem : function(e, _self, o)
17285     {
17286         e.preventDefault();
17287         
17288         this.lastItem = Roo.apply([], this.item);
17289         
17290         var index = this.item.indexOf(o.data) * 1;
17291         
17292         if( index < 0){
17293             Roo.log('not this item?!');
17294             return;
17295         }
17296         
17297         this.item.splice(index, 1);
17298         o.item.remove();
17299         
17300         this.syncValue();
17301         
17302         this.fireEvent('remove', this, e);
17303         
17304         this.validate();
17305         
17306     },
17307     
17308     syncValue : function()
17309     {
17310         if(!this.item.length){
17311             this.clearValue();
17312             return;
17313         }
17314             
17315         var value = [];
17316         var _this = this;
17317         Roo.each(this.item, function(i){
17318             if(_this.valueField){
17319                 value.push(i[_this.valueField]);
17320                 return;
17321             }
17322
17323             value.push(i);
17324         });
17325
17326         this.value = value.join(',');
17327
17328         if(this.hiddenField){
17329             this.hiddenField.dom.value = this.value;
17330         }
17331         
17332         this.store.fireEvent("datachanged", this.store);
17333         
17334         this.validate();
17335     },
17336     
17337     clearItem : function()
17338     {
17339         if(!this.multiple){
17340             return;
17341         }
17342         
17343         this.item = [];
17344         
17345         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17346            c.remove();
17347         });
17348         
17349         this.syncValue();
17350         
17351         this.validate();
17352         
17353         if(this.tickable && !Roo.isTouch){
17354             this.view.refresh();
17355         }
17356     },
17357     
17358     inputEl: function ()
17359     {
17360         if(Roo.isIOS && this.useNativeIOS){
17361             return this.el.select('select.roo-ios-select', true).first();
17362         }
17363         
17364         if(Roo.isTouch && this.mobileTouchView){
17365             return this.el.select('input.form-control',true).first();
17366         }
17367         
17368         if(this.tickable){
17369             return this.searchField;
17370         }
17371         
17372         return this.el.select('input.form-control',true).first();
17373     },
17374     
17375     onTickableFooterButtonClick : function(e, btn, el)
17376     {
17377         e.preventDefault();
17378         
17379         this.lastItem = Roo.apply([], this.item);
17380         
17381         if(btn && btn.name == 'cancel'){
17382             this.tickItems = Roo.apply([], this.item);
17383             this.collapse();
17384             return;
17385         }
17386         
17387         this.clearItem();
17388         
17389         var _this = this;
17390         
17391         Roo.each(this.tickItems, function(o){
17392             _this.addItem(o);
17393         });
17394         
17395         this.collapse();
17396         
17397     },
17398     
17399     validate : function()
17400     {
17401         if(this.getVisibilityEl().hasClass('hidden')){
17402             return true;
17403         }
17404         
17405         var v = this.getRawValue();
17406         
17407         if(this.multiple){
17408             v = this.getValue();
17409         }
17410         
17411         if(this.disabled || this.allowBlank || v.length){
17412             this.markValid();
17413             return true;
17414         }
17415         
17416         this.markInvalid();
17417         return false;
17418     },
17419     
17420     tickableInputEl : function()
17421     {
17422         if(!this.tickable || !this.editable){
17423             return this.inputEl();
17424         }
17425         
17426         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17427     },
17428     
17429     
17430     getAutoCreateTouchView : function()
17431     {
17432         var id = Roo.id();
17433         
17434         var cfg = {
17435             cls: 'form-group' //input-group
17436         };
17437         
17438         var input =  {
17439             tag: 'input',
17440             id : id,
17441             type : this.inputType,
17442             cls : 'form-control x-combo-noedit',
17443             autocomplete: 'new-password',
17444             placeholder : this.placeholder || '',
17445             readonly : true
17446         };
17447         
17448         if (this.name) {
17449             input.name = this.name;
17450         }
17451         
17452         if (this.size) {
17453             input.cls += ' input-' + this.size;
17454         }
17455         
17456         if (this.disabled) {
17457             input.disabled = true;
17458         }
17459         
17460         var inputblock = {
17461             cls : 'roo-combobox-wrap',
17462             cn : [
17463                 input
17464             ]
17465         };
17466         
17467         if(this.before){
17468             inputblock.cls += ' input-group';
17469             
17470             inputblock.cn.unshift({
17471                 tag :'span',
17472                 cls : 'input-group-addon input-group-prepend input-group-text',
17473                 html : this.before
17474             });
17475         }
17476         
17477         if(this.removable && !this.multiple){
17478             inputblock.cls += ' roo-removable';
17479             
17480             inputblock.cn.push({
17481                 tag: 'button',
17482                 html : 'x',
17483                 cls : 'roo-combo-removable-btn close'
17484             });
17485         }
17486
17487         if(this.hasFeedback && !this.allowBlank){
17488             
17489             inputblock.cls += ' has-feedback';
17490             
17491             inputblock.cn.push({
17492                 tag: 'span',
17493                 cls: 'glyphicon form-control-feedback'
17494             });
17495             
17496         }
17497         
17498         if (this.after) {
17499             
17500             inputblock.cls += (this.before) ? '' : ' input-group';
17501             
17502             inputblock.cn.push({
17503                 tag :'span',
17504                 cls : 'input-group-addon input-group-append input-group-text',
17505                 html : this.after
17506             });
17507         }
17508
17509         
17510         var ibwrap = inputblock;
17511         
17512         if(this.multiple){
17513             ibwrap = {
17514                 tag: 'ul',
17515                 cls: 'roo-select2-choices',
17516                 cn:[
17517                     {
17518                         tag: 'li',
17519                         cls: 'roo-select2-search-field',
17520                         cn: [
17521
17522                             inputblock
17523                         ]
17524                     }
17525                 ]
17526             };
17527         
17528             
17529         }
17530         
17531         var combobox = {
17532             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17533             cn: [
17534                 {
17535                     tag: 'input',
17536                     type : 'hidden',
17537                     cls: 'form-hidden-field'
17538                 },
17539                 ibwrap
17540             ]
17541         };
17542         
17543         if(!this.multiple && this.showToggleBtn){
17544             
17545             var caret = {
17546                 cls: 'caret'
17547             };
17548             
17549             if (this.caret != false) {
17550                 caret = {
17551                      tag: 'i',
17552                      cls: 'fa fa-' + this.caret
17553                 };
17554                 
17555             }
17556             
17557             combobox.cn.push({
17558                 tag :'span',
17559                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17560                 cn : [
17561                     Roo.bootstrap.version == 3 ? caret : '',
17562                     {
17563                         tag: 'span',
17564                         cls: 'combobox-clear',
17565                         cn  : [
17566                             {
17567                                 tag : 'i',
17568                                 cls: 'icon-remove'
17569                             }
17570                         ]
17571                     }
17572                 ]
17573
17574             })
17575         }
17576         
17577         if(this.multiple){
17578             combobox.cls += ' roo-select2-container-multi';
17579         }
17580         
17581         var align = this.labelAlign || this.parentLabelAlign();
17582         
17583         if (align ==='left' && this.fieldLabel.length) {
17584
17585             cfg.cn = [
17586                 {
17587                    tag : 'i',
17588                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17589                    tooltip : 'This field is required'
17590                 },
17591                 {
17592                     tag: 'label',
17593                     cls : 'control-label col-form-label',
17594                     html : this.fieldLabel
17595
17596                 },
17597                 {
17598                     cls : 'roo-combobox-wrap ', 
17599                     cn: [
17600                         combobox
17601                     ]
17602                 }
17603             ];
17604             
17605             var labelCfg = cfg.cn[1];
17606             var contentCfg = cfg.cn[2];
17607             
17608
17609             if(this.indicatorpos == 'right'){
17610                 cfg.cn = [
17611                     {
17612                         tag: 'label',
17613                         'for' :  id,
17614                         cls : 'control-label col-form-label',
17615                         cn : [
17616                             {
17617                                 tag : 'span',
17618                                 html : this.fieldLabel
17619                             },
17620                             {
17621                                 tag : 'i',
17622                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17623                                 tooltip : 'This field is required'
17624                             }
17625                         ]
17626                     },
17627                     {
17628                         cls : "roo-combobox-wrap ",
17629                         cn: [
17630                             combobox
17631                         ]
17632                     }
17633
17634                 ];
17635                 
17636                 labelCfg = cfg.cn[0];
17637                 contentCfg = cfg.cn[1];
17638             }
17639             
17640            
17641             
17642             if(this.labelWidth > 12){
17643                 labelCfg.style = "width: " + this.labelWidth + 'px';
17644             }
17645            
17646             if(this.labelWidth < 13 && this.labelmd == 0){
17647                 this.labelmd = this.labelWidth;
17648             }
17649             
17650             if(this.labellg > 0){
17651                 labelCfg.cls += ' col-lg-' + this.labellg;
17652                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17653             }
17654             
17655             if(this.labelmd > 0){
17656                 labelCfg.cls += ' col-md-' + this.labelmd;
17657                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17658             }
17659             
17660             if(this.labelsm > 0){
17661                 labelCfg.cls += ' col-sm-' + this.labelsm;
17662                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17663             }
17664             
17665             if(this.labelxs > 0){
17666                 labelCfg.cls += ' col-xs-' + this.labelxs;
17667                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17668             }
17669                 
17670                 
17671         } else if ( this.fieldLabel.length) {
17672             cfg.cn = [
17673                 {
17674                    tag : 'i',
17675                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17676                    tooltip : 'This field is required'
17677                 },
17678                 {
17679                     tag: 'label',
17680                     cls : 'control-label',
17681                     html : this.fieldLabel
17682
17683                 },
17684                 {
17685                     cls : '', 
17686                     cn: [
17687                         combobox
17688                     ]
17689                 }
17690             ];
17691             
17692             if(this.indicatorpos == 'right'){
17693                 cfg.cn = [
17694                     {
17695                         tag: 'label',
17696                         cls : 'control-label',
17697                         html : this.fieldLabel,
17698                         cn : [
17699                             {
17700                                tag : 'i',
17701                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17702                                tooltip : 'This field is required'
17703                             }
17704                         ]
17705                     },
17706                     {
17707                         cls : '', 
17708                         cn: [
17709                             combobox
17710                         ]
17711                     }
17712                 ];
17713             }
17714         } else {
17715             cfg.cn = combobox;    
17716         }
17717         
17718         
17719         var settings = this;
17720         
17721         ['xs','sm','md','lg'].map(function(size){
17722             if (settings[size]) {
17723                 cfg.cls += ' col-' + size + '-' + settings[size];
17724             }
17725         });
17726         
17727         return cfg;
17728     },
17729     
17730     initTouchView : function()
17731     {
17732         this.renderTouchView();
17733         
17734         this.touchViewEl.on('scroll', function(){
17735             this.el.dom.scrollTop = 0;
17736         }, this);
17737         
17738         this.originalValue = this.getValue();
17739         
17740         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17741         
17742         this.inputEl().on("click", this.showTouchView, this);
17743         if (this.triggerEl) {
17744             this.triggerEl.on("click", this.showTouchView, this);
17745         }
17746         
17747         
17748         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17749         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17750         
17751         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17752         
17753         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17754         this.store.on('load', this.onTouchViewLoad, this);
17755         this.store.on('loadexception', this.onTouchViewLoadException, this);
17756         
17757         if(this.hiddenName){
17758             
17759             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17760             
17761             this.hiddenField.dom.value =
17762                 this.hiddenValue !== undefined ? this.hiddenValue :
17763                 this.value !== undefined ? this.value : '';
17764         
17765             this.el.dom.removeAttribute('name');
17766             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17767         }
17768         
17769         if(this.multiple){
17770             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17771             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17772         }
17773         
17774         if(this.removable && !this.multiple){
17775             var close = this.closeTriggerEl();
17776             if(close){
17777                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17778                 close.on('click', this.removeBtnClick, this, close);
17779             }
17780         }
17781         /*
17782          * fix the bug in Safari iOS8
17783          */
17784         this.inputEl().on("focus", function(e){
17785             document.activeElement.blur();
17786         }, this);
17787         
17788         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17789         
17790         return;
17791         
17792         
17793     },
17794     
17795     renderTouchView : function()
17796     {
17797         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17798         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17799         
17800         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17801         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17802         
17803         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17804         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17805         this.touchViewBodyEl.setStyle('overflow', 'auto');
17806         
17807         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17808         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17809         
17810         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17811         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17812         
17813     },
17814     
17815     showTouchView : function()
17816     {
17817         if(this.disabled){
17818             return;
17819         }
17820         
17821         this.touchViewHeaderEl.hide();
17822
17823         if(this.modalTitle.length){
17824             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17825             this.touchViewHeaderEl.show();
17826         }
17827
17828         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17829         this.touchViewEl.show();
17830
17831         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17832         
17833         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17834         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17835
17836         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17837
17838         if(this.modalTitle.length){
17839             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17840         }
17841         
17842         this.touchViewBodyEl.setHeight(bodyHeight);
17843
17844         if(this.animate){
17845             var _this = this;
17846             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17847         }else{
17848             this.touchViewEl.addClass(['in','show']);
17849         }
17850         
17851         if(this._touchViewMask){
17852             Roo.get(document.body).addClass("x-body-masked");
17853             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17854             this._touchViewMask.setStyle('z-index', 10000);
17855             this._touchViewMask.addClass('show');
17856         }
17857         
17858         this.doTouchViewQuery();
17859         
17860     },
17861     
17862     hideTouchView : function()
17863     {
17864         this.touchViewEl.removeClass(['in','show']);
17865
17866         if(this.animate){
17867             var _this = this;
17868             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17869         }else{
17870             this.touchViewEl.setStyle('display', 'none');
17871         }
17872         
17873         if(this._touchViewMask){
17874             this._touchViewMask.removeClass('show');
17875             Roo.get(document.body).removeClass("x-body-masked");
17876         }
17877     },
17878     
17879     setTouchViewValue : function()
17880     {
17881         if(this.multiple){
17882             this.clearItem();
17883         
17884             var _this = this;
17885
17886             Roo.each(this.tickItems, function(o){
17887                 this.addItem(o);
17888             }, this);
17889         }
17890         
17891         this.hideTouchView();
17892     },
17893     
17894     doTouchViewQuery : function()
17895     {
17896         var qe = {
17897             query: '',
17898             forceAll: true,
17899             combo: this,
17900             cancel:false
17901         };
17902         
17903         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17904             return false;
17905         }
17906         
17907         if(!this.alwaysQuery || this.mode == 'local'){
17908             this.onTouchViewLoad();
17909             return;
17910         }
17911         
17912         this.store.load();
17913     },
17914     
17915     onTouchViewBeforeLoad : function(combo,opts)
17916     {
17917         return;
17918     },
17919
17920     // private
17921     onTouchViewLoad : function()
17922     {
17923         if(this.store.getCount() < 1){
17924             this.onTouchViewEmptyResults();
17925             return;
17926         }
17927         
17928         this.clearTouchView();
17929         
17930         var rawValue = this.getRawValue();
17931         
17932         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17933         
17934         this.tickItems = [];
17935         
17936         this.store.data.each(function(d, rowIndex){
17937             var row = this.touchViewListGroup.createChild(template);
17938             
17939             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17940                 row.addClass(d.data.cls);
17941             }
17942             
17943             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17944                 var cfg = {
17945                     data : d.data,
17946                     html : d.data[this.displayField]
17947                 };
17948                 
17949                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17950                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17951                 }
17952             }
17953             row.removeClass('selected');
17954             if(!this.multiple && this.valueField &&
17955                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17956             {
17957                 // radio buttons..
17958                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17959                 row.addClass('selected');
17960             }
17961             
17962             if(this.multiple && this.valueField &&
17963                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17964             {
17965                 
17966                 // checkboxes...
17967                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17968                 this.tickItems.push(d.data);
17969             }
17970             
17971             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17972             
17973         }, this);
17974         
17975         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17976         
17977         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17978
17979         if(this.modalTitle.length){
17980             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17981         }
17982
17983         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17984         
17985         if(this.mobile_restrict_height && listHeight < bodyHeight){
17986             this.touchViewBodyEl.setHeight(listHeight);
17987         }
17988         
17989         var _this = this;
17990         
17991         if(firstChecked && listHeight > bodyHeight){
17992             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17993         }
17994         
17995     },
17996     
17997     onTouchViewLoadException : function()
17998     {
17999         this.hideTouchView();
18000     },
18001     
18002     onTouchViewEmptyResults : function()
18003     {
18004         this.clearTouchView();
18005         
18006         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18007         
18008         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18009         
18010     },
18011     
18012     clearTouchView : function()
18013     {
18014         this.touchViewListGroup.dom.innerHTML = '';
18015     },
18016     
18017     onTouchViewClick : function(e, el, o)
18018     {
18019         e.preventDefault();
18020         
18021         var row = o.row;
18022         var rowIndex = o.rowIndex;
18023         
18024         var r = this.store.getAt(rowIndex);
18025         
18026         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18027             
18028             if(!this.multiple){
18029                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18030                     c.dom.removeAttribute('checked');
18031                 }, this);
18032
18033                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18034
18035                 this.setFromData(r.data);
18036
18037                 var close = this.closeTriggerEl();
18038
18039                 if(close){
18040                     close.show();
18041                 }
18042
18043                 this.hideTouchView();
18044
18045                 this.fireEvent('select', this, r, rowIndex);
18046
18047                 return;
18048             }
18049
18050             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18051                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18052                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18053                 return;
18054             }
18055
18056             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18057             this.addItem(r.data);
18058             this.tickItems.push(r.data);
18059         }
18060     },
18061     
18062     getAutoCreateNativeIOS : function()
18063     {
18064         var cfg = {
18065             cls: 'form-group' //input-group,
18066         };
18067         
18068         var combobox =  {
18069             tag: 'select',
18070             cls : 'roo-ios-select'
18071         };
18072         
18073         if (this.name) {
18074             combobox.name = this.name;
18075         }
18076         
18077         if (this.disabled) {
18078             combobox.disabled = true;
18079         }
18080         
18081         var settings = this;
18082         
18083         ['xs','sm','md','lg'].map(function(size){
18084             if (settings[size]) {
18085                 cfg.cls += ' col-' + size + '-' + settings[size];
18086             }
18087         });
18088         
18089         cfg.cn = combobox;
18090         
18091         return cfg;
18092         
18093     },
18094     
18095     initIOSView : function()
18096     {
18097         this.store.on('load', this.onIOSViewLoad, this);
18098         
18099         return;
18100     },
18101     
18102     onIOSViewLoad : function()
18103     {
18104         if(this.store.getCount() < 1){
18105             return;
18106         }
18107         
18108         this.clearIOSView();
18109         
18110         if(this.allowBlank) {
18111             
18112             var default_text = '-- SELECT --';
18113             
18114             if(this.placeholder.length){
18115                 default_text = this.placeholder;
18116             }
18117             
18118             if(this.emptyTitle.length){
18119                 default_text += ' - ' + this.emptyTitle + ' -';
18120             }
18121             
18122             var opt = this.inputEl().createChild({
18123                 tag: 'option',
18124                 value : 0,
18125                 html : default_text
18126             });
18127             
18128             var o = {};
18129             o[this.valueField] = 0;
18130             o[this.displayField] = default_text;
18131             
18132             this.ios_options.push({
18133                 data : o,
18134                 el : opt
18135             });
18136             
18137         }
18138         
18139         this.store.data.each(function(d, rowIndex){
18140             
18141             var html = '';
18142             
18143             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18144                 html = d.data[this.displayField];
18145             }
18146             
18147             var value = '';
18148             
18149             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18150                 value = d.data[this.valueField];
18151             }
18152             
18153             var option = {
18154                 tag: 'option',
18155                 value : value,
18156                 html : html
18157             };
18158             
18159             if(this.value == d.data[this.valueField]){
18160                 option['selected'] = true;
18161             }
18162             
18163             var opt = this.inputEl().createChild(option);
18164             
18165             this.ios_options.push({
18166                 data : d.data,
18167                 el : opt
18168             });
18169             
18170         }, this);
18171         
18172         this.inputEl().on('change', function(){
18173            this.fireEvent('select', this);
18174         }, this);
18175         
18176     },
18177     
18178     clearIOSView: function()
18179     {
18180         this.inputEl().dom.innerHTML = '';
18181         
18182         this.ios_options = [];
18183     },
18184     
18185     setIOSValue: function(v)
18186     {
18187         this.value = v;
18188         
18189         if(!this.ios_options){
18190             return;
18191         }
18192         
18193         Roo.each(this.ios_options, function(opts){
18194            
18195            opts.el.dom.removeAttribute('selected');
18196            
18197            if(opts.data[this.valueField] != v){
18198                return;
18199            }
18200            
18201            opts.el.dom.setAttribute('selected', true);
18202            
18203         }, this);
18204     }
18205
18206     /** 
18207     * @cfg {Boolean} grow 
18208     * @hide 
18209     */
18210     /** 
18211     * @cfg {Number} growMin 
18212     * @hide 
18213     */
18214     /** 
18215     * @cfg {Number} growMax 
18216     * @hide 
18217     */
18218     /**
18219      * @hide
18220      * @method autoSize
18221      */
18222 });
18223
18224 Roo.apply(Roo.bootstrap.ComboBox,  {
18225     
18226     header : {
18227         tag: 'div',
18228         cls: 'modal-header',
18229         cn: [
18230             {
18231                 tag: 'h4',
18232                 cls: 'modal-title'
18233             }
18234         ]
18235     },
18236     
18237     body : {
18238         tag: 'div',
18239         cls: 'modal-body',
18240         cn: [
18241             {
18242                 tag: 'ul',
18243                 cls: 'list-group'
18244             }
18245         ]
18246     },
18247     
18248     listItemRadio : {
18249         tag: 'li',
18250         cls: 'list-group-item',
18251         cn: [
18252             {
18253                 tag: 'span',
18254                 cls: 'roo-combobox-list-group-item-value'
18255             },
18256             {
18257                 tag: 'div',
18258                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18259                 cn: [
18260                     {
18261                         tag: 'input',
18262                         type: 'radio'
18263                     },
18264                     {
18265                         tag: 'label'
18266                     }
18267                 ]
18268             }
18269         ]
18270     },
18271     
18272     listItemCheckbox : {
18273         tag: 'li',
18274         cls: 'list-group-item',
18275         cn: [
18276             {
18277                 tag: 'span',
18278                 cls: 'roo-combobox-list-group-item-value'
18279             },
18280             {
18281                 tag: 'div',
18282                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18283                 cn: [
18284                     {
18285                         tag: 'input',
18286                         type: 'checkbox'
18287                     },
18288                     {
18289                         tag: 'label'
18290                     }
18291                 ]
18292             }
18293         ]
18294     },
18295     
18296     emptyResult : {
18297         tag: 'div',
18298         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18299     },
18300     
18301     footer : {
18302         tag: 'div',
18303         cls: 'modal-footer',
18304         cn: [
18305             {
18306                 tag: 'div',
18307                 cls: 'row',
18308                 cn: [
18309                     {
18310                         tag: 'div',
18311                         cls: 'col-xs-6 text-left',
18312                         cn: {
18313                             tag: 'button',
18314                             cls: 'btn btn-danger roo-touch-view-cancel',
18315                             html: 'Cancel'
18316                         }
18317                     },
18318                     {
18319                         tag: 'div',
18320                         cls: 'col-xs-6 text-right',
18321                         cn: {
18322                             tag: 'button',
18323                             cls: 'btn btn-success roo-touch-view-ok',
18324                             html: 'OK'
18325                         }
18326                     }
18327                 ]
18328             }
18329         ]
18330         
18331     }
18332 });
18333
18334 Roo.apply(Roo.bootstrap.ComboBox,  {
18335     
18336     touchViewTemplate : {
18337         tag: 'div',
18338         cls: 'modal fade roo-combobox-touch-view',
18339         cn: [
18340             {
18341                 tag: 'div',
18342                 cls: 'modal-dialog',
18343                 style : 'position:fixed', // we have to fix position....
18344                 cn: [
18345                     {
18346                         tag: 'div',
18347                         cls: 'modal-content',
18348                         cn: [
18349                             Roo.bootstrap.ComboBox.header,
18350                             Roo.bootstrap.ComboBox.body,
18351                             Roo.bootstrap.ComboBox.footer
18352                         ]
18353                     }
18354                 ]
18355             }
18356         ]
18357     }
18358 });/*
18359  * Based on:
18360  * Ext JS Library 1.1.1
18361  * Copyright(c) 2006-2007, Ext JS, LLC.
18362  *
18363  * Originally Released Under LGPL - original licence link has changed is not relivant.
18364  *
18365  * Fork - LGPL
18366  * <script type="text/javascript">
18367  */
18368
18369 /**
18370  * @class Roo.View
18371  * @extends Roo.util.Observable
18372  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18373  * This class also supports single and multi selection modes. <br>
18374  * Create a data model bound view:
18375  <pre><code>
18376  var store = new Roo.data.Store(...);
18377
18378  var view = new Roo.View({
18379     el : "my-element",
18380     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18381  
18382     singleSelect: true,
18383     selectedClass: "ydataview-selected",
18384     store: store
18385  });
18386
18387  // listen for node click?
18388  view.on("click", function(vw, index, node, e){
18389  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18390  });
18391
18392  // load XML data
18393  dataModel.load("foobar.xml");
18394  </code></pre>
18395  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18396  * <br><br>
18397  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18398  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18399  * 
18400  * Note: old style constructor is still suported (container, template, config)
18401  * 
18402  * @constructor
18403  * Create a new View
18404  * @param {Object} config The config object
18405  * 
18406  */
18407 Roo.View = function(config, depreciated_tpl, depreciated_config){
18408     
18409     this.parent = false;
18410     
18411     if (typeof(depreciated_tpl) == 'undefined') {
18412         // new way.. - universal constructor.
18413         Roo.apply(this, config);
18414         this.el  = Roo.get(this.el);
18415     } else {
18416         // old format..
18417         this.el  = Roo.get(config);
18418         this.tpl = depreciated_tpl;
18419         Roo.apply(this, depreciated_config);
18420     }
18421     this.wrapEl  = this.el.wrap().wrap();
18422     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18423     
18424     
18425     if(typeof(this.tpl) == "string"){
18426         this.tpl = new Roo.Template(this.tpl);
18427     } else {
18428         // support xtype ctors..
18429         this.tpl = new Roo.factory(this.tpl, Roo);
18430     }
18431     
18432     
18433     this.tpl.compile();
18434     
18435     /** @private */
18436     this.addEvents({
18437         /**
18438          * @event beforeclick
18439          * Fires before a click is processed. Returns false to cancel the default action.
18440          * @param {Roo.View} this
18441          * @param {Number} index The index of the target node
18442          * @param {HTMLElement} node The target node
18443          * @param {Roo.EventObject} e The raw event object
18444          */
18445             "beforeclick" : true,
18446         /**
18447          * @event click
18448          * Fires when a template node is clicked.
18449          * @param {Roo.View} this
18450          * @param {Number} index The index of the target node
18451          * @param {HTMLElement} node The target node
18452          * @param {Roo.EventObject} e The raw event object
18453          */
18454             "click" : true,
18455         /**
18456          * @event dblclick
18457          * Fires when a template node is double clicked.
18458          * @param {Roo.View} this
18459          * @param {Number} index The index of the target node
18460          * @param {HTMLElement} node The target node
18461          * @param {Roo.EventObject} e The raw event object
18462          */
18463             "dblclick" : true,
18464         /**
18465          * @event contextmenu
18466          * Fires when a template node is right clicked.
18467          * @param {Roo.View} this
18468          * @param {Number} index The index of the target node
18469          * @param {HTMLElement} node The target node
18470          * @param {Roo.EventObject} e The raw event object
18471          */
18472             "contextmenu" : true,
18473         /**
18474          * @event selectionchange
18475          * Fires when the selected nodes change.
18476          * @param {Roo.View} this
18477          * @param {Array} selections Array of the selected nodes
18478          */
18479             "selectionchange" : true,
18480     
18481         /**
18482          * @event beforeselect
18483          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18484          * @param {Roo.View} this
18485          * @param {HTMLElement} node The node to be selected
18486          * @param {Array} selections Array of currently selected nodes
18487          */
18488             "beforeselect" : true,
18489         /**
18490          * @event preparedata
18491          * Fires on every row to render, to allow you to change the data.
18492          * @param {Roo.View} this
18493          * @param {Object} data to be rendered (change this)
18494          */
18495           "preparedata" : true
18496           
18497           
18498         });
18499
18500
18501
18502     this.el.on({
18503         "click": this.onClick,
18504         "dblclick": this.onDblClick,
18505         "contextmenu": this.onContextMenu,
18506         scope:this
18507     });
18508
18509     this.selections = [];
18510     this.nodes = [];
18511     this.cmp = new Roo.CompositeElementLite([]);
18512     if(this.store){
18513         this.store = Roo.factory(this.store, Roo.data);
18514         this.setStore(this.store, true);
18515     }
18516     
18517     if ( this.footer && this.footer.xtype) {
18518            
18519          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18520         
18521         this.footer.dataSource = this.store;
18522         this.footer.container = fctr;
18523         this.footer = Roo.factory(this.footer, Roo);
18524         fctr.insertFirst(this.el);
18525         
18526         // this is a bit insane - as the paging toolbar seems to detach the el..
18527 //        dom.parentNode.parentNode.parentNode
18528          // they get detached?
18529     }
18530     
18531     
18532     Roo.View.superclass.constructor.call(this);
18533     
18534     
18535 };
18536
18537 Roo.extend(Roo.View, Roo.util.Observable, {
18538     
18539      /**
18540      * @cfg {Roo.data.Store} store Data store to load data from.
18541      */
18542     store : false,
18543     
18544     /**
18545      * @cfg {String|Roo.Element} el The container element.
18546      */
18547     el : '',
18548     
18549     /**
18550      * @cfg {String|Roo.Template} tpl The template used by this View 
18551      */
18552     tpl : false,
18553     /**
18554      * @cfg {String} dataName the named area of the template to use as the data area
18555      *                          Works with domtemplates roo-name="name"
18556      */
18557     dataName: false,
18558     /**
18559      * @cfg {String} selectedClass The css class to add to selected nodes
18560      */
18561     selectedClass : "x-view-selected",
18562      /**
18563      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18564      */
18565     emptyText : "",
18566     
18567     /**
18568      * @cfg {String} text to display on mask (default Loading)
18569      */
18570     mask : false,
18571     /**
18572      * @cfg {Boolean} multiSelect Allow multiple selection
18573      */
18574     multiSelect : false,
18575     /**
18576      * @cfg {Boolean} singleSelect Allow single selection
18577      */
18578     singleSelect:  false,
18579     
18580     /**
18581      * @cfg {Boolean} toggleSelect - selecting 
18582      */
18583     toggleSelect : false,
18584     
18585     /**
18586      * @cfg {Boolean} tickable - selecting 
18587      */
18588     tickable : false,
18589     
18590     /**
18591      * Returns the element this view is bound to.
18592      * @return {Roo.Element}
18593      */
18594     getEl : function(){
18595         return this.wrapEl;
18596     },
18597     
18598     
18599
18600     /**
18601      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18602      */
18603     refresh : function(){
18604         //Roo.log('refresh');
18605         var t = this.tpl;
18606         
18607         // if we are using something like 'domtemplate', then
18608         // the what gets used is:
18609         // t.applySubtemplate(NAME, data, wrapping data..)
18610         // the outer template then get' applied with
18611         //     the store 'extra data'
18612         // and the body get's added to the
18613         //      roo-name="data" node?
18614         //      <span class='roo-tpl-{name}'></span> ?????
18615         
18616         
18617         
18618         this.clearSelections();
18619         this.el.update("");
18620         var html = [];
18621         var records = this.store.getRange();
18622         if(records.length < 1) {
18623             
18624             // is this valid??  = should it render a template??
18625             
18626             this.el.update(this.emptyText);
18627             return;
18628         }
18629         var el = this.el;
18630         if (this.dataName) {
18631             this.el.update(t.apply(this.store.meta)); //????
18632             el = this.el.child('.roo-tpl-' + this.dataName);
18633         }
18634         
18635         for(var i = 0, len = records.length; i < len; i++){
18636             var data = this.prepareData(records[i].data, i, records[i]);
18637             this.fireEvent("preparedata", this, data, i, records[i]);
18638             
18639             var d = Roo.apply({}, data);
18640             
18641             if(this.tickable){
18642                 Roo.apply(d, {'roo-id' : Roo.id()});
18643                 
18644                 var _this = this;
18645             
18646                 Roo.each(this.parent.item, function(item){
18647                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18648                         return;
18649                     }
18650                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18651                 });
18652             }
18653             
18654             html[html.length] = Roo.util.Format.trim(
18655                 this.dataName ?
18656                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18657                     t.apply(d)
18658             );
18659         }
18660         
18661         
18662         
18663         el.update(html.join(""));
18664         this.nodes = el.dom.childNodes;
18665         this.updateIndexes(0);
18666     },
18667     
18668
18669     /**
18670      * Function to override to reformat the data that is sent to
18671      * the template for each node.
18672      * DEPRICATED - use the preparedata event handler.
18673      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18674      * a JSON object for an UpdateManager bound view).
18675      */
18676     prepareData : function(data, index, record)
18677     {
18678         this.fireEvent("preparedata", this, data, index, record);
18679         return data;
18680     },
18681
18682     onUpdate : function(ds, record){
18683         // Roo.log('on update');   
18684         this.clearSelections();
18685         var index = this.store.indexOf(record);
18686         var n = this.nodes[index];
18687         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18688         n.parentNode.removeChild(n);
18689         this.updateIndexes(index, index);
18690     },
18691
18692     
18693     
18694 // --------- FIXME     
18695     onAdd : function(ds, records, index)
18696     {
18697         //Roo.log(['on Add', ds, records, index] );        
18698         this.clearSelections();
18699         if(this.nodes.length == 0){
18700             this.refresh();
18701             return;
18702         }
18703         var n = this.nodes[index];
18704         for(var i = 0, len = records.length; i < len; i++){
18705             var d = this.prepareData(records[i].data, i, records[i]);
18706             if(n){
18707                 this.tpl.insertBefore(n, d);
18708             }else{
18709                 
18710                 this.tpl.append(this.el, d);
18711             }
18712         }
18713         this.updateIndexes(index);
18714     },
18715
18716     onRemove : function(ds, record, index){
18717        // Roo.log('onRemove');
18718         this.clearSelections();
18719         var el = this.dataName  ?
18720             this.el.child('.roo-tpl-' + this.dataName) :
18721             this.el; 
18722         
18723         el.dom.removeChild(this.nodes[index]);
18724         this.updateIndexes(index);
18725     },
18726
18727     /**
18728      * Refresh an individual node.
18729      * @param {Number} index
18730      */
18731     refreshNode : function(index){
18732         this.onUpdate(this.store, this.store.getAt(index));
18733     },
18734
18735     updateIndexes : function(startIndex, endIndex){
18736         var ns = this.nodes;
18737         startIndex = startIndex || 0;
18738         endIndex = endIndex || ns.length - 1;
18739         for(var i = startIndex; i <= endIndex; i++){
18740             ns[i].nodeIndex = i;
18741         }
18742     },
18743
18744     /**
18745      * Changes the data store this view uses and refresh the view.
18746      * @param {Store} store
18747      */
18748     setStore : function(store, initial){
18749         if(!initial && this.store){
18750             this.store.un("datachanged", this.refresh);
18751             this.store.un("add", this.onAdd);
18752             this.store.un("remove", this.onRemove);
18753             this.store.un("update", this.onUpdate);
18754             this.store.un("clear", this.refresh);
18755             this.store.un("beforeload", this.onBeforeLoad);
18756             this.store.un("load", this.onLoad);
18757             this.store.un("loadexception", this.onLoad);
18758         }
18759         if(store){
18760           
18761             store.on("datachanged", this.refresh, this);
18762             store.on("add", this.onAdd, this);
18763             store.on("remove", this.onRemove, this);
18764             store.on("update", this.onUpdate, this);
18765             store.on("clear", this.refresh, this);
18766             store.on("beforeload", this.onBeforeLoad, this);
18767             store.on("load", this.onLoad, this);
18768             store.on("loadexception", this.onLoad, this);
18769         }
18770         
18771         if(store){
18772             this.refresh();
18773         }
18774     },
18775     /**
18776      * onbeforeLoad - masks the loading area.
18777      *
18778      */
18779     onBeforeLoad : function(store,opts)
18780     {
18781          //Roo.log('onBeforeLoad');   
18782         if (!opts.add) {
18783             this.el.update("");
18784         }
18785         this.el.mask(this.mask ? this.mask : "Loading" ); 
18786     },
18787     onLoad : function ()
18788     {
18789         this.el.unmask();
18790     },
18791     
18792
18793     /**
18794      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18795      * @param {HTMLElement} node
18796      * @return {HTMLElement} The template node
18797      */
18798     findItemFromChild : function(node){
18799         var el = this.dataName  ?
18800             this.el.child('.roo-tpl-' + this.dataName,true) :
18801             this.el.dom; 
18802         
18803         if(!node || node.parentNode == el){
18804                     return node;
18805             }
18806             var p = node.parentNode;
18807             while(p && p != el){
18808             if(p.parentNode == el){
18809                 return p;
18810             }
18811             p = p.parentNode;
18812         }
18813             return null;
18814     },
18815
18816     /** @ignore */
18817     onClick : function(e){
18818         var item = this.findItemFromChild(e.getTarget());
18819         if(item){
18820             var index = this.indexOf(item);
18821             if(this.onItemClick(item, index, e) !== false){
18822                 this.fireEvent("click", this, index, item, e);
18823             }
18824         }else{
18825             this.clearSelections();
18826         }
18827     },
18828
18829     /** @ignore */
18830     onContextMenu : function(e){
18831         var item = this.findItemFromChild(e.getTarget());
18832         if(item){
18833             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18834         }
18835     },
18836
18837     /** @ignore */
18838     onDblClick : function(e){
18839         var item = this.findItemFromChild(e.getTarget());
18840         if(item){
18841             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18842         }
18843     },
18844
18845     onItemClick : function(item, index, e)
18846     {
18847         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18848             return false;
18849         }
18850         if (this.toggleSelect) {
18851             var m = this.isSelected(item) ? 'unselect' : 'select';
18852             //Roo.log(m);
18853             var _t = this;
18854             _t[m](item, true, false);
18855             return true;
18856         }
18857         if(this.multiSelect || this.singleSelect){
18858             if(this.multiSelect && e.shiftKey && this.lastSelection){
18859                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18860             }else{
18861                 this.select(item, this.multiSelect && e.ctrlKey);
18862                 this.lastSelection = item;
18863             }
18864             
18865             if(!this.tickable){
18866                 e.preventDefault();
18867             }
18868             
18869         }
18870         return true;
18871     },
18872
18873     /**
18874      * Get the number of selected nodes.
18875      * @return {Number}
18876      */
18877     getSelectionCount : function(){
18878         return this.selections.length;
18879     },
18880
18881     /**
18882      * Get the currently selected nodes.
18883      * @return {Array} An array of HTMLElements
18884      */
18885     getSelectedNodes : function(){
18886         return this.selections;
18887     },
18888
18889     /**
18890      * Get the indexes of the selected nodes.
18891      * @return {Array}
18892      */
18893     getSelectedIndexes : function(){
18894         var indexes = [], s = this.selections;
18895         for(var i = 0, len = s.length; i < len; i++){
18896             indexes.push(s[i].nodeIndex);
18897         }
18898         return indexes;
18899     },
18900
18901     /**
18902      * Clear all selections
18903      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18904      */
18905     clearSelections : function(suppressEvent){
18906         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18907             this.cmp.elements = this.selections;
18908             this.cmp.removeClass(this.selectedClass);
18909             this.selections = [];
18910             if(!suppressEvent){
18911                 this.fireEvent("selectionchange", this, this.selections);
18912             }
18913         }
18914     },
18915
18916     /**
18917      * Returns true if the passed node is selected
18918      * @param {HTMLElement/Number} node The node or node index
18919      * @return {Boolean}
18920      */
18921     isSelected : function(node){
18922         var s = this.selections;
18923         if(s.length < 1){
18924             return false;
18925         }
18926         node = this.getNode(node);
18927         return s.indexOf(node) !== -1;
18928     },
18929
18930     /**
18931      * Selects nodes.
18932      * @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
18933      * @param {Boolean} keepExisting (optional) true to keep existing selections
18934      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18935      */
18936     select : function(nodeInfo, keepExisting, suppressEvent){
18937         if(nodeInfo instanceof Array){
18938             if(!keepExisting){
18939                 this.clearSelections(true);
18940             }
18941             for(var i = 0, len = nodeInfo.length; i < len; i++){
18942                 this.select(nodeInfo[i], true, true);
18943             }
18944             return;
18945         } 
18946         var node = this.getNode(nodeInfo);
18947         if(!node || this.isSelected(node)){
18948             return; // already selected.
18949         }
18950         if(!keepExisting){
18951             this.clearSelections(true);
18952         }
18953         
18954         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18955             Roo.fly(node).addClass(this.selectedClass);
18956             this.selections.push(node);
18957             if(!suppressEvent){
18958                 this.fireEvent("selectionchange", this, this.selections);
18959             }
18960         }
18961         
18962         
18963     },
18964       /**
18965      * Unselects nodes.
18966      * @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
18967      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18968      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18969      */
18970     unselect : function(nodeInfo, keepExisting, suppressEvent)
18971     {
18972         if(nodeInfo instanceof Array){
18973             Roo.each(this.selections, function(s) {
18974                 this.unselect(s, nodeInfo);
18975             }, this);
18976             return;
18977         }
18978         var node = this.getNode(nodeInfo);
18979         if(!node || !this.isSelected(node)){
18980             //Roo.log("not selected");
18981             return; // not selected.
18982         }
18983         // fireevent???
18984         var ns = [];
18985         Roo.each(this.selections, function(s) {
18986             if (s == node ) {
18987                 Roo.fly(node).removeClass(this.selectedClass);
18988
18989                 return;
18990             }
18991             ns.push(s);
18992         },this);
18993         
18994         this.selections= ns;
18995         this.fireEvent("selectionchange", this, this.selections);
18996     },
18997
18998     /**
18999      * Gets a template node.
19000      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19001      * @return {HTMLElement} The node or null if it wasn't found
19002      */
19003     getNode : function(nodeInfo){
19004         if(typeof nodeInfo == "string"){
19005             return document.getElementById(nodeInfo);
19006         }else if(typeof nodeInfo == "number"){
19007             return this.nodes[nodeInfo];
19008         }
19009         return nodeInfo;
19010     },
19011
19012     /**
19013      * Gets a range template nodes.
19014      * @param {Number} startIndex
19015      * @param {Number} endIndex
19016      * @return {Array} An array of nodes
19017      */
19018     getNodes : function(start, end){
19019         var ns = this.nodes;
19020         start = start || 0;
19021         end = typeof end == "undefined" ? ns.length - 1 : end;
19022         var nodes = [];
19023         if(start <= end){
19024             for(var i = start; i <= end; i++){
19025                 nodes.push(ns[i]);
19026             }
19027         } else{
19028             for(var i = start; i >= end; i--){
19029                 nodes.push(ns[i]);
19030             }
19031         }
19032         return nodes;
19033     },
19034
19035     /**
19036      * Finds the index of the passed node
19037      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19038      * @return {Number} The index of the node or -1
19039      */
19040     indexOf : function(node){
19041         node = this.getNode(node);
19042         if(typeof node.nodeIndex == "number"){
19043             return node.nodeIndex;
19044         }
19045         var ns = this.nodes;
19046         for(var i = 0, len = ns.length; i < len; i++){
19047             if(ns[i] == node){
19048                 return i;
19049             }
19050         }
19051         return -1;
19052     }
19053 });
19054 /*
19055  * - LGPL
19056  *
19057  * based on jquery fullcalendar
19058  * 
19059  */
19060
19061 Roo.bootstrap = Roo.bootstrap || {};
19062 /**
19063  * @class Roo.bootstrap.Calendar
19064  * @extends Roo.bootstrap.Component
19065  * Bootstrap Calendar class
19066  * @cfg {Boolean} loadMask (true|false) default false
19067  * @cfg {Object} header generate the user specific header of the calendar, default false
19068
19069  * @constructor
19070  * Create a new Container
19071  * @param {Object} config The config object
19072  */
19073
19074
19075
19076 Roo.bootstrap.Calendar = function(config){
19077     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19078      this.addEvents({
19079         /**
19080              * @event select
19081              * Fires when a date is selected
19082              * @param {DatePicker} this
19083              * @param {Date} date The selected date
19084              */
19085         'select': true,
19086         /**
19087              * @event monthchange
19088              * Fires when the displayed month changes 
19089              * @param {DatePicker} this
19090              * @param {Date} date The selected month
19091              */
19092         'monthchange': true,
19093         /**
19094              * @event evententer
19095              * Fires when mouse over an event
19096              * @param {Calendar} this
19097              * @param {event} Event
19098              */
19099         'evententer': true,
19100         /**
19101              * @event eventleave
19102              * Fires when the mouse leaves an
19103              * @param {Calendar} this
19104              * @param {event}
19105              */
19106         'eventleave': true,
19107         /**
19108              * @event eventclick
19109              * Fires when the mouse click an
19110              * @param {Calendar} this
19111              * @param {event}
19112              */
19113         'eventclick': true
19114         
19115     });
19116
19117 };
19118
19119 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19120     
19121      /**
19122      * @cfg {Number} startDay
19123      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19124      */
19125     startDay : 0,
19126     
19127     loadMask : false,
19128     
19129     header : false,
19130       
19131     getAutoCreate : function(){
19132         
19133         
19134         var fc_button = function(name, corner, style, content ) {
19135             return Roo.apply({},{
19136                 tag : 'span',
19137                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19138                          (corner.length ?
19139                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19140                             ''
19141                         ),
19142                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19143                 unselectable: 'on'
19144             });
19145         };
19146         
19147         var header = {};
19148         
19149         if(!this.header){
19150             header = {
19151                 tag : 'table',
19152                 cls : 'fc-header',
19153                 style : 'width:100%',
19154                 cn : [
19155                     {
19156                         tag: 'tr',
19157                         cn : [
19158                             {
19159                                 tag : 'td',
19160                                 cls : 'fc-header-left',
19161                                 cn : [
19162                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19163                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19164                                     { tag: 'span', cls: 'fc-header-space' },
19165                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19166
19167
19168                                 ]
19169                             },
19170
19171                             {
19172                                 tag : 'td',
19173                                 cls : 'fc-header-center',
19174                                 cn : [
19175                                     {
19176                                         tag: 'span',
19177                                         cls: 'fc-header-title',
19178                                         cn : {
19179                                             tag: 'H2',
19180                                             html : 'month / year'
19181                                         }
19182                                     }
19183
19184                                 ]
19185                             },
19186                             {
19187                                 tag : 'td',
19188                                 cls : 'fc-header-right',
19189                                 cn : [
19190                               /*      fc_button('month', 'left', '', 'month' ),
19191                                     fc_button('week', '', '', 'week' ),
19192                                     fc_button('day', 'right', '', 'day' )
19193                                 */    
19194
19195                                 ]
19196                             }
19197
19198                         ]
19199                     }
19200                 ]
19201             };
19202         }
19203         
19204         header = this.header;
19205         
19206        
19207         var cal_heads = function() {
19208             var ret = [];
19209             // fixme - handle this.
19210             
19211             for (var i =0; i < Date.dayNames.length; i++) {
19212                 var d = Date.dayNames[i];
19213                 ret.push({
19214                     tag: 'th',
19215                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19216                     html : d.substring(0,3)
19217                 });
19218                 
19219             }
19220             ret[0].cls += ' fc-first';
19221             ret[6].cls += ' fc-last';
19222             return ret;
19223         };
19224         var cal_cell = function(n) {
19225             return  {
19226                 tag: 'td',
19227                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19228                 cn : [
19229                     {
19230                         cn : [
19231                             {
19232                                 cls: 'fc-day-number',
19233                                 html: 'D'
19234                             },
19235                             {
19236                                 cls: 'fc-day-content',
19237                              
19238                                 cn : [
19239                                      {
19240                                         style: 'position: relative;' // height: 17px;
19241                                     }
19242                                 ]
19243                             }
19244                             
19245                             
19246                         ]
19247                     }
19248                 ]
19249                 
19250             }
19251         };
19252         var cal_rows = function() {
19253             
19254             var ret = [];
19255             for (var r = 0; r < 6; r++) {
19256                 var row= {
19257                     tag : 'tr',
19258                     cls : 'fc-week',
19259                     cn : []
19260                 };
19261                 
19262                 for (var i =0; i < Date.dayNames.length; i++) {
19263                     var d = Date.dayNames[i];
19264                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19265
19266                 }
19267                 row.cn[0].cls+=' fc-first';
19268                 row.cn[0].cn[0].style = 'min-height:90px';
19269                 row.cn[6].cls+=' fc-last';
19270                 ret.push(row);
19271                 
19272             }
19273             ret[0].cls += ' fc-first';
19274             ret[4].cls += ' fc-prev-last';
19275             ret[5].cls += ' fc-last';
19276             return ret;
19277             
19278         };
19279         
19280         var cal_table = {
19281             tag: 'table',
19282             cls: 'fc-border-separate',
19283             style : 'width:100%',
19284             cellspacing  : 0,
19285             cn : [
19286                 { 
19287                     tag: 'thead',
19288                     cn : [
19289                         { 
19290                             tag: 'tr',
19291                             cls : 'fc-first fc-last',
19292                             cn : cal_heads()
19293                         }
19294                     ]
19295                 },
19296                 { 
19297                     tag: 'tbody',
19298                     cn : cal_rows()
19299                 }
19300                   
19301             ]
19302         };
19303          
19304          var cfg = {
19305             cls : 'fc fc-ltr',
19306             cn : [
19307                 header,
19308                 {
19309                     cls : 'fc-content',
19310                     style : "position: relative;",
19311                     cn : [
19312                         {
19313                             cls : 'fc-view fc-view-month fc-grid',
19314                             style : 'position: relative',
19315                             unselectable : 'on',
19316                             cn : [
19317                                 {
19318                                     cls : 'fc-event-container',
19319                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19320                                 },
19321                                 cal_table
19322                             ]
19323                         }
19324                     ]
19325     
19326                 }
19327            ] 
19328             
19329         };
19330         
19331          
19332         
19333         return cfg;
19334     },
19335     
19336     
19337     initEvents : function()
19338     {
19339         if(!this.store){
19340             throw "can not find store for calendar";
19341         }
19342         
19343         var mark = {
19344             tag: "div",
19345             cls:"x-dlg-mask",
19346             style: "text-align:center",
19347             cn: [
19348                 {
19349                     tag: "div",
19350                     style: "background-color:white;width:50%;margin:250 auto",
19351                     cn: [
19352                         {
19353                             tag: "img",
19354                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19355                         },
19356                         {
19357                             tag: "span",
19358                             html: "Loading"
19359                         }
19360                         
19361                     ]
19362                 }
19363             ]
19364         };
19365         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19366         
19367         var size = this.el.select('.fc-content', true).first().getSize();
19368         this.maskEl.setSize(size.width, size.height);
19369         this.maskEl.enableDisplayMode("block");
19370         if(!this.loadMask){
19371             this.maskEl.hide();
19372         }
19373         
19374         this.store = Roo.factory(this.store, Roo.data);
19375         this.store.on('load', this.onLoad, this);
19376         this.store.on('beforeload', this.onBeforeLoad, this);
19377         
19378         this.resize();
19379         
19380         this.cells = this.el.select('.fc-day',true);
19381         //Roo.log(this.cells);
19382         this.textNodes = this.el.query('.fc-day-number');
19383         this.cells.addClassOnOver('fc-state-hover');
19384         
19385         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19386         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19387         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19388         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19389         
19390         this.on('monthchange', this.onMonthChange, this);
19391         
19392         this.update(new Date().clearTime());
19393     },
19394     
19395     resize : function() {
19396         var sz  = this.el.getSize();
19397         
19398         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19399         this.el.select('.fc-day-content div',true).setHeight(34);
19400     },
19401     
19402     
19403     // private
19404     showPrevMonth : function(e){
19405         this.update(this.activeDate.add("mo", -1));
19406     },
19407     showToday : function(e){
19408         this.update(new Date().clearTime());
19409     },
19410     // private
19411     showNextMonth : function(e){
19412         this.update(this.activeDate.add("mo", 1));
19413     },
19414
19415     // private
19416     showPrevYear : function(){
19417         this.update(this.activeDate.add("y", -1));
19418     },
19419
19420     // private
19421     showNextYear : function(){
19422         this.update(this.activeDate.add("y", 1));
19423     },
19424
19425     
19426    // private
19427     update : function(date)
19428     {
19429         var vd = this.activeDate;
19430         this.activeDate = date;
19431 //        if(vd && this.el){
19432 //            var t = date.getTime();
19433 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19434 //                Roo.log('using add remove');
19435 //                
19436 //                this.fireEvent('monthchange', this, date);
19437 //                
19438 //                this.cells.removeClass("fc-state-highlight");
19439 //                this.cells.each(function(c){
19440 //                   if(c.dateValue == t){
19441 //                       c.addClass("fc-state-highlight");
19442 //                       setTimeout(function(){
19443 //                            try{c.dom.firstChild.focus();}catch(e){}
19444 //                       }, 50);
19445 //                       return false;
19446 //                   }
19447 //                   return true;
19448 //                });
19449 //                return;
19450 //            }
19451 //        }
19452         
19453         var days = date.getDaysInMonth();
19454         
19455         var firstOfMonth = date.getFirstDateOfMonth();
19456         var startingPos = firstOfMonth.getDay()-this.startDay;
19457         
19458         if(startingPos < this.startDay){
19459             startingPos += 7;
19460         }
19461         
19462         var pm = date.add(Date.MONTH, -1);
19463         var prevStart = pm.getDaysInMonth()-startingPos;
19464 //        
19465         this.cells = this.el.select('.fc-day',true);
19466         this.textNodes = this.el.query('.fc-day-number');
19467         this.cells.addClassOnOver('fc-state-hover');
19468         
19469         var cells = this.cells.elements;
19470         var textEls = this.textNodes;
19471         
19472         Roo.each(cells, function(cell){
19473             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19474         });
19475         
19476         days += startingPos;
19477
19478         // convert everything to numbers so it's fast
19479         var day = 86400000;
19480         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19481         //Roo.log(d);
19482         //Roo.log(pm);
19483         //Roo.log(prevStart);
19484         
19485         var today = new Date().clearTime().getTime();
19486         var sel = date.clearTime().getTime();
19487         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19488         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19489         var ddMatch = this.disabledDatesRE;
19490         var ddText = this.disabledDatesText;
19491         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19492         var ddaysText = this.disabledDaysText;
19493         var format = this.format;
19494         
19495         var setCellClass = function(cal, cell){
19496             cell.row = 0;
19497             cell.events = [];
19498             cell.more = [];
19499             //Roo.log('set Cell Class');
19500             cell.title = "";
19501             var t = d.getTime();
19502             
19503             //Roo.log(d);
19504             
19505             cell.dateValue = t;
19506             if(t == today){
19507                 cell.className += " fc-today";
19508                 cell.className += " fc-state-highlight";
19509                 cell.title = cal.todayText;
19510             }
19511             if(t == sel){
19512                 // disable highlight in other month..
19513                 //cell.className += " fc-state-highlight";
19514                 
19515             }
19516             // disabling
19517             if(t < min) {
19518                 cell.className = " fc-state-disabled";
19519                 cell.title = cal.minText;
19520                 return;
19521             }
19522             if(t > max) {
19523                 cell.className = " fc-state-disabled";
19524                 cell.title = cal.maxText;
19525                 return;
19526             }
19527             if(ddays){
19528                 if(ddays.indexOf(d.getDay()) != -1){
19529                     cell.title = ddaysText;
19530                     cell.className = " fc-state-disabled";
19531                 }
19532             }
19533             if(ddMatch && format){
19534                 var fvalue = d.dateFormat(format);
19535                 if(ddMatch.test(fvalue)){
19536                     cell.title = ddText.replace("%0", fvalue);
19537                     cell.className = " fc-state-disabled";
19538                 }
19539             }
19540             
19541             if (!cell.initialClassName) {
19542                 cell.initialClassName = cell.dom.className;
19543             }
19544             
19545             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19546         };
19547
19548         var i = 0;
19549         
19550         for(; i < startingPos; i++) {
19551             textEls[i].innerHTML = (++prevStart);
19552             d.setDate(d.getDate()+1);
19553             
19554             cells[i].className = "fc-past fc-other-month";
19555             setCellClass(this, cells[i]);
19556         }
19557         
19558         var intDay = 0;
19559         
19560         for(; i < days; i++){
19561             intDay = i - startingPos + 1;
19562             textEls[i].innerHTML = (intDay);
19563             d.setDate(d.getDate()+1);
19564             
19565             cells[i].className = ''; // "x-date-active";
19566             setCellClass(this, cells[i]);
19567         }
19568         var extraDays = 0;
19569         
19570         for(; i < 42; i++) {
19571             textEls[i].innerHTML = (++extraDays);
19572             d.setDate(d.getDate()+1);
19573             
19574             cells[i].className = "fc-future fc-other-month";
19575             setCellClass(this, cells[i]);
19576         }
19577         
19578         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19579         
19580         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19581         
19582         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19583         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19584         
19585         if(totalRows != 6){
19586             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19587             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19588         }
19589         
19590         this.fireEvent('monthchange', this, date);
19591         
19592         
19593         /*
19594         if(!this.internalRender){
19595             var main = this.el.dom.firstChild;
19596             var w = main.offsetWidth;
19597             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19598             Roo.fly(main).setWidth(w);
19599             this.internalRender = true;
19600             // opera does not respect the auto grow header center column
19601             // then, after it gets a width opera refuses to recalculate
19602             // without a second pass
19603             if(Roo.isOpera && !this.secondPass){
19604                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19605                 this.secondPass = true;
19606                 this.update.defer(10, this, [date]);
19607             }
19608         }
19609         */
19610         
19611     },
19612     
19613     findCell : function(dt) {
19614         dt = dt.clearTime().getTime();
19615         var ret = false;
19616         this.cells.each(function(c){
19617             //Roo.log("check " +c.dateValue + '?=' + dt);
19618             if(c.dateValue == dt){
19619                 ret = c;
19620                 return false;
19621             }
19622             return true;
19623         });
19624         
19625         return ret;
19626     },
19627     
19628     findCells : function(ev) {
19629         var s = ev.start.clone().clearTime().getTime();
19630        // Roo.log(s);
19631         var e= ev.end.clone().clearTime().getTime();
19632        // Roo.log(e);
19633         var ret = [];
19634         this.cells.each(function(c){
19635              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19636             
19637             if(c.dateValue > e){
19638                 return ;
19639             }
19640             if(c.dateValue < s){
19641                 return ;
19642             }
19643             ret.push(c);
19644         });
19645         
19646         return ret;    
19647     },
19648     
19649 //    findBestRow: function(cells)
19650 //    {
19651 //        var ret = 0;
19652 //        
19653 //        for (var i =0 ; i < cells.length;i++) {
19654 //            ret  = Math.max(cells[i].rows || 0,ret);
19655 //        }
19656 //        return ret;
19657 //        
19658 //    },
19659     
19660     
19661     addItem : function(ev)
19662     {
19663         // look for vertical location slot in
19664         var cells = this.findCells(ev);
19665         
19666 //        ev.row = this.findBestRow(cells);
19667         
19668         // work out the location.
19669         
19670         var crow = false;
19671         var rows = [];
19672         for(var i =0; i < cells.length; i++) {
19673             
19674             cells[i].row = cells[0].row;
19675             
19676             if(i == 0){
19677                 cells[i].row = cells[i].row + 1;
19678             }
19679             
19680             if (!crow) {
19681                 crow = {
19682                     start : cells[i],
19683                     end :  cells[i]
19684                 };
19685                 continue;
19686             }
19687             if (crow.start.getY() == cells[i].getY()) {
19688                 // on same row.
19689                 crow.end = cells[i];
19690                 continue;
19691             }
19692             // different row.
19693             rows.push(crow);
19694             crow = {
19695                 start: cells[i],
19696                 end : cells[i]
19697             };
19698             
19699         }
19700         
19701         rows.push(crow);
19702         ev.els = [];
19703         ev.rows = rows;
19704         ev.cells = cells;
19705         
19706         cells[0].events.push(ev);
19707         
19708         this.calevents.push(ev);
19709     },
19710     
19711     clearEvents: function() {
19712         
19713         if(!this.calevents){
19714             return;
19715         }
19716         
19717         Roo.each(this.cells.elements, function(c){
19718             c.row = 0;
19719             c.events = [];
19720             c.more = [];
19721         });
19722         
19723         Roo.each(this.calevents, function(e) {
19724             Roo.each(e.els, function(el) {
19725                 el.un('mouseenter' ,this.onEventEnter, this);
19726                 el.un('mouseleave' ,this.onEventLeave, this);
19727                 el.remove();
19728             },this);
19729         },this);
19730         
19731         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19732             e.remove();
19733         });
19734         
19735     },
19736     
19737     renderEvents: function()
19738     {   
19739         var _this = this;
19740         
19741         this.cells.each(function(c) {
19742             
19743             if(c.row < 5){
19744                 return;
19745             }
19746             
19747             var ev = c.events;
19748             
19749             var r = 4;
19750             if(c.row != c.events.length){
19751                 r = 4 - (4 - (c.row - c.events.length));
19752             }
19753             
19754             c.events = ev.slice(0, r);
19755             c.more = ev.slice(r);
19756             
19757             if(c.more.length && c.more.length == 1){
19758                 c.events.push(c.more.pop());
19759             }
19760             
19761             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19762             
19763         });
19764             
19765         this.cells.each(function(c) {
19766             
19767             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19768             
19769             
19770             for (var e = 0; e < c.events.length; e++){
19771                 var ev = c.events[e];
19772                 var rows = ev.rows;
19773                 
19774                 for(var i = 0; i < rows.length; i++) {
19775                 
19776                     // how many rows should it span..
19777
19778                     var  cfg = {
19779                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19780                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19781
19782                         unselectable : "on",
19783                         cn : [
19784                             {
19785                                 cls: 'fc-event-inner',
19786                                 cn : [
19787     //                                {
19788     //                                  tag:'span',
19789     //                                  cls: 'fc-event-time',
19790     //                                  html : cells.length > 1 ? '' : ev.time
19791     //                                },
19792                                     {
19793                                       tag:'span',
19794                                       cls: 'fc-event-title',
19795                                       html : String.format('{0}', ev.title)
19796                                     }
19797
19798
19799                                 ]
19800                             },
19801                             {
19802                                 cls: 'ui-resizable-handle ui-resizable-e',
19803                                 html : '&nbsp;&nbsp;&nbsp'
19804                             }
19805
19806                         ]
19807                     };
19808
19809                     if (i == 0) {
19810                         cfg.cls += ' fc-event-start';
19811                     }
19812                     if ((i+1) == rows.length) {
19813                         cfg.cls += ' fc-event-end';
19814                     }
19815
19816                     var ctr = _this.el.select('.fc-event-container',true).first();
19817                     var cg = ctr.createChild(cfg);
19818
19819                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19820                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19821
19822                     var r = (c.more.length) ? 1 : 0;
19823                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19824                     cg.setWidth(ebox.right - sbox.x -2);
19825
19826                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19827                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19828                     cg.on('click', _this.onEventClick, _this, ev);
19829
19830                     ev.els.push(cg);
19831                     
19832                 }
19833                 
19834             }
19835             
19836             
19837             if(c.more.length){
19838                 var  cfg = {
19839                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19840                     style : 'position: absolute',
19841                     unselectable : "on",
19842                     cn : [
19843                         {
19844                             cls: 'fc-event-inner',
19845                             cn : [
19846                                 {
19847                                   tag:'span',
19848                                   cls: 'fc-event-title',
19849                                   html : 'More'
19850                                 }
19851
19852
19853                             ]
19854                         },
19855                         {
19856                             cls: 'ui-resizable-handle ui-resizable-e',
19857                             html : '&nbsp;&nbsp;&nbsp'
19858                         }
19859
19860                     ]
19861                 };
19862
19863                 var ctr = _this.el.select('.fc-event-container',true).first();
19864                 var cg = ctr.createChild(cfg);
19865
19866                 var sbox = c.select('.fc-day-content',true).first().getBox();
19867                 var ebox = c.select('.fc-day-content',true).first().getBox();
19868                 //Roo.log(cg);
19869                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19870                 cg.setWidth(ebox.right - sbox.x -2);
19871
19872                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19873                 
19874             }
19875             
19876         });
19877         
19878         
19879         
19880     },
19881     
19882     onEventEnter: function (e, el,event,d) {
19883         this.fireEvent('evententer', this, el, event);
19884     },
19885     
19886     onEventLeave: function (e, el,event,d) {
19887         this.fireEvent('eventleave', this, el, event);
19888     },
19889     
19890     onEventClick: function (e, el,event,d) {
19891         this.fireEvent('eventclick', this, el, event);
19892     },
19893     
19894     onMonthChange: function () {
19895         this.store.load();
19896     },
19897     
19898     onMoreEventClick: function(e, el, more)
19899     {
19900         var _this = this;
19901         
19902         this.calpopover.placement = 'right';
19903         this.calpopover.setTitle('More');
19904         
19905         this.calpopover.setContent('');
19906         
19907         var ctr = this.calpopover.el.select('.popover-content', true).first();
19908         
19909         Roo.each(more, function(m){
19910             var cfg = {
19911                 cls : 'fc-event-hori fc-event-draggable',
19912                 html : m.title
19913             };
19914             var cg = ctr.createChild(cfg);
19915             
19916             cg.on('click', _this.onEventClick, _this, m);
19917         });
19918         
19919         this.calpopover.show(el);
19920         
19921         
19922     },
19923     
19924     onLoad: function () 
19925     {   
19926         this.calevents = [];
19927         var cal = this;
19928         
19929         if(this.store.getCount() > 0){
19930             this.store.data.each(function(d){
19931                cal.addItem({
19932                     id : d.data.id,
19933                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19934                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19935                     time : d.data.start_time,
19936                     title : d.data.title,
19937                     description : d.data.description,
19938                     venue : d.data.venue
19939                 });
19940             });
19941         }
19942         
19943         this.renderEvents();
19944         
19945         if(this.calevents.length && this.loadMask){
19946             this.maskEl.hide();
19947         }
19948     },
19949     
19950     onBeforeLoad: function()
19951     {
19952         this.clearEvents();
19953         if(this.loadMask){
19954             this.maskEl.show();
19955         }
19956     }
19957 });
19958
19959  
19960  /*
19961  * - LGPL
19962  *
19963  * element
19964  * 
19965  */
19966
19967 /**
19968  * @class Roo.bootstrap.Popover
19969  * @extends Roo.bootstrap.Component
19970  * Bootstrap Popover class
19971  * @cfg {String} html contents of the popover   (or false to use children..)
19972  * @cfg {String} title of popover (or false to hide)
19973  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19974  * @cfg {String} trigger click || hover (or false to trigger manually)
19975  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19976  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19977  *      - if false and it has a 'parent' then it will be automatically added to that element
19978  *      - if string - Roo.get  will be called 
19979  * @cfg {Number} delay - delay before showing
19980  
19981  * @constructor
19982  * Create a new Popover
19983  * @param {Object} config The config object
19984  */
19985
19986 Roo.bootstrap.Popover = function(config){
19987     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19988     
19989     this.addEvents({
19990         // raw events
19991          /**
19992          * @event show
19993          * After the popover show
19994          * 
19995          * @param {Roo.bootstrap.Popover} this
19996          */
19997         "show" : true,
19998         /**
19999          * @event hide
20000          * After the popover hide
20001          * 
20002          * @param {Roo.bootstrap.Popover} this
20003          */
20004         "hide" : true
20005     });
20006 };
20007
20008 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20009     
20010     title: false,
20011     html: false,
20012     
20013     placement : 'right',
20014     trigger : 'hover', // hover
20015     modal : false,
20016     delay : 0,
20017     
20018     over: false,
20019     
20020     can_build_overlaid : false,
20021     
20022     maskEl : false, // the mask element
20023     headerEl : false,
20024     contentEl : false,
20025     alignEl : false, // when show is called with an element - this get's stored.
20026     
20027     getChildContainer : function()
20028     {
20029         return this.contentEl;
20030         
20031     },
20032     getPopoverHeader : function()
20033     {
20034         this.title = true; // flag not to hide it..
20035         this.headerEl.addClass('p-0');
20036         return this.headerEl
20037     },
20038     
20039     
20040     getAutoCreate : function(){
20041          
20042         var cfg = {
20043            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20044            style: 'display:block',
20045            cn : [
20046                 {
20047                     cls : 'arrow'
20048                 },
20049                 {
20050                     cls : 'popover-inner ',
20051                     cn : [
20052                         {
20053                             tag: 'h3',
20054                             cls: 'popover-title popover-header',
20055                             html : this.title === false ? '' : this.title
20056                         },
20057                         {
20058                             cls : 'popover-content popover-body '  + (this.cls || ''),
20059                             html : this.html || ''
20060                         }
20061                     ]
20062                     
20063                 }
20064            ]
20065         };
20066         
20067         return cfg;
20068     },
20069     /**
20070      * @param {string} the title
20071      */
20072     setTitle: function(str)
20073     {
20074         this.title = str;
20075         if (this.el) {
20076             this.headerEl.dom.innerHTML = str;
20077         }
20078         
20079     },
20080     /**
20081      * @param {string} the body content
20082      */
20083     setContent: function(str)
20084     {
20085         this.html = str;
20086         if (this.contentEl) {
20087             this.contentEl.dom.innerHTML = str;
20088         }
20089         
20090     },
20091     // as it get's added to the bottom of the page.
20092     onRender : function(ct, position)
20093     {
20094         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20095         
20096         
20097         
20098         if(!this.el){
20099             var cfg = Roo.apply({},  this.getAutoCreate());
20100             cfg.id = Roo.id();
20101             
20102             if (this.cls) {
20103                 cfg.cls += ' ' + this.cls;
20104             }
20105             if (this.style) {
20106                 cfg.style = this.style;
20107             }
20108             //Roo.log("adding to ");
20109             this.el = Roo.get(document.body).createChild(cfg, position);
20110 //            Roo.log(this.el);
20111         }
20112         
20113         this.contentEl = this.el.select('.popover-content',true).first();
20114         this.headerEl =  this.el.select('.popover-title',true).first();
20115         
20116         var nitems = [];
20117         if(typeof(this.items) != 'undefined'){
20118             var items = this.items;
20119             delete this.items;
20120
20121             for(var i =0;i < items.length;i++) {
20122                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20123             }
20124         }
20125
20126         this.items = nitems;
20127         
20128         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20129         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20130         
20131         
20132         
20133         this.initEvents();
20134     },
20135     
20136     resizeMask : function()
20137     {
20138         this.maskEl.setSize(
20139             Roo.lib.Dom.getViewWidth(true),
20140             Roo.lib.Dom.getViewHeight(true)
20141         );
20142     },
20143     
20144     initEvents : function()
20145     {
20146         
20147         if (!this.modal) { 
20148             Roo.bootstrap.Popover.register(this);
20149         }
20150          
20151         this.arrowEl = this.el.select('.arrow',true).first();
20152         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20153         this.el.enableDisplayMode('block');
20154         this.el.hide();
20155  
20156         
20157         if (this.over === false && !this.parent()) {
20158             return; 
20159         }
20160         if (this.triggers === false) {
20161             return;
20162         }
20163          
20164         // support parent
20165         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20166         var triggers = this.trigger ? this.trigger.split(' ') : [];
20167         Roo.each(triggers, function(trigger) {
20168         
20169             if (trigger == 'click') {
20170                 on_el.on('click', this.toggle, this);
20171             } else if (trigger != 'manual') {
20172                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20173                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20174       
20175                 on_el.on(eventIn  ,this.enter, this);
20176                 on_el.on(eventOut, this.leave, this);
20177             }
20178         }, this);
20179     },
20180     
20181     
20182     // private
20183     timeout : null,
20184     hoverState : null,
20185     
20186     toggle : function () {
20187         this.hoverState == 'in' ? this.leave() : this.enter();
20188     },
20189     
20190     enter : function () {
20191         
20192         clearTimeout(this.timeout);
20193     
20194         this.hoverState = 'in';
20195     
20196         if (!this.delay || !this.delay.show) {
20197             this.show();
20198             return;
20199         }
20200         var _t = this;
20201         this.timeout = setTimeout(function () {
20202             if (_t.hoverState == 'in') {
20203                 _t.show();
20204             }
20205         }, this.delay.show)
20206     },
20207     
20208     leave : function() {
20209         clearTimeout(this.timeout);
20210     
20211         this.hoverState = 'out';
20212     
20213         if (!this.delay || !this.delay.hide) {
20214             this.hide();
20215             return;
20216         }
20217         var _t = this;
20218         this.timeout = setTimeout(function () {
20219             if (_t.hoverState == 'out') {
20220                 _t.hide();
20221             }
20222         }, this.delay.hide)
20223     },
20224     /**
20225      * Show the popover
20226      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20227      * @param {string} (left|right|top|bottom) position
20228      */
20229     show : function (on_el, placement)
20230     {
20231         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20232         on_el = on_el || false; // default to false
20233          
20234         if (!on_el) {
20235             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20236                 on_el = this.parent().el;
20237             } else if (this.over) {
20238                 on_el = Roo.get(this.over);
20239             }
20240             
20241         }
20242         
20243         this.alignEl = Roo.get( on_el );
20244
20245         if (!this.el) {
20246             this.render(document.body);
20247         }
20248         
20249         
20250          
20251         
20252         if (this.title === false) {
20253             this.headerEl.hide();
20254         }
20255         
20256        
20257         this.el.show();
20258         this.el.dom.style.display = 'block';
20259          
20260  
20261         if (this.alignEl) {
20262             this.updatePosition(this.placement, true);
20263              
20264         } else {
20265             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20266             var es = this.el.getSize();
20267             var x = Roo.lib.Dom.getViewWidth()/2;
20268             var y = Roo.lib.Dom.getViewHeight()/2;
20269             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20270             
20271         }
20272
20273         
20274         //var arrow = this.el.select('.arrow',true).first();
20275         //arrow.set(align[2], 
20276         
20277         this.el.addClass('in');
20278         
20279          
20280         
20281         this.hoverState = 'in';
20282         
20283         if (this.modal) {
20284             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20285             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20286             this.maskEl.dom.style.display = 'block';
20287             this.maskEl.addClass('show');
20288         }
20289         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20290  
20291         this.fireEvent('show', this);
20292         
20293     },
20294     /**
20295      * fire this manually after loading a grid in the table for example
20296      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20297      * @param {Boolean} try and move it if we cant get right position.
20298      */
20299     updatePosition : function(placement, try_move)
20300     {
20301         // allow for calling with no parameters
20302         placement = placement   ? placement :  this.placement;
20303         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20304         
20305         this.el.removeClass([
20306             'fade','top','bottom', 'left', 'right','in',
20307             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20308         ]);
20309         this.el.addClass(placement + ' bs-popover-' + placement);
20310         
20311         if (!this.alignEl ) {
20312             return false;
20313         }
20314         
20315         switch (placement) {
20316             case 'right':
20317                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20318                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20319                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20320                     //normal display... or moved up/down.
20321                     this.el.setXY(offset);
20322                     var xy = this.alignEl.getAnchorXY('tr', false);
20323                     xy[0]+=2;xy[1]+=5;
20324                     this.arrowEl.setXY(xy);
20325                     return true;
20326                 }
20327                 // continue through...
20328                 return this.updatePosition('left', false);
20329                 
20330             
20331             case 'left':
20332                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20333                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20334                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20335                     //normal display... or moved up/down.
20336                     this.el.setXY(offset);
20337                     var xy = this.alignEl.getAnchorXY('tl', false);
20338                     xy[0]-=10;xy[1]+=5; // << fix me
20339                     this.arrowEl.setXY(xy);
20340                     return true;
20341                 }
20342                 // call self...
20343                 return this.updatePosition('right', false);
20344             
20345             case 'top':
20346                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20347                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20348                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20349                     //normal display... or moved up/down.
20350                     this.el.setXY(offset);
20351                     var xy = this.alignEl.getAnchorXY('t', false);
20352                     xy[1]-=10; // << fix me
20353                     this.arrowEl.setXY(xy);
20354                     return true;
20355                 }
20356                 // fall through
20357                return this.updatePosition('bottom', false);
20358             
20359             case 'bottom':
20360                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20361                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20362                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20363                     //normal display... or moved up/down.
20364                     this.el.setXY(offset);
20365                     var xy = this.alignEl.getAnchorXY('b', false);
20366                      xy[1]+=2; // << fix me
20367                     this.arrowEl.setXY(xy);
20368                     return true;
20369                 }
20370                 // fall through
20371                 return this.updatePosition('top', false);
20372                 
20373             
20374         }
20375         
20376         
20377         return false;
20378     },
20379     
20380     hide : function()
20381     {
20382         this.el.setXY([0,0]);
20383         this.el.removeClass('in');
20384         this.el.hide();
20385         this.hoverState = null;
20386         this.maskEl.hide(); // always..
20387         this.fireEvent('hide', this);
20388     }
20389     
20390 });
20391
20392
20393 Roo.apply(Roo.bootstrap.Popover, {
20394
20395     alignment : {
20396         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20397         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20398         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20399         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20400     },
20401     
20402     zIndex : 20001,
20403
20404     clickHander : false,
20405     
20406
20407     onMouseDown : function(e)
20408     {
20409         if (!e.getTarget(".roo-popover")) {
20410             this.hideAll();
20411         }
20412          
20413     },
20414     
20415     popups : [],
20416     
20417     register : function(popup)
20418     {
20419         if (!Roo.bootstrap.Popover.clickHandler) {
20420             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20421         }
20422         // hide other popups.
20423         this.hideAll();
20424         this.popups.push(popup);
20425     },
20426     hideAll : function()
20427     {
20428         this.popups.forEach(function(p) {
20429             p.hide();
20430         });
20431     }
20432
20433 });/*
20434  * - LGPL
20435  *
20436  * Card header - holder for the card header elements.
20437  * 
20438  */
20439
20440 /**
20441  * @class Roo.bootstrap.PopoverNav
20442  * @extends Roo.bootstrap.NavGroup
20443  * Bootstrap Popover header navigation class
20444  * @constructor
20445  * Create a new Popover Header Navigation 
20446  * @param {Object} config The config object
20447  */
20448
20449 Roo.bootstrap.PopoverNav = function(config){
20450     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20451 };
20452
20453 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20454     
20455     
20456     container_method : 'getPopoverHeader' 
20457     
20458      
20459     
20460     
20461    
20462 });
20463
20464  
20465
20466  /*
20467  * - LGPL
20468  *
20469  * Progress
20470  * 
20471  */
20472
20473 /**
20474  * @class Roo.bootstrap.Progress
20475  * @extends Roo.bootstrap.Component
20476  * Bootstrap Progress class
20477  * @cfg {Boolean} striped striped of the progress bar
20478  * @cfg {Boolean} active animated of the progress bar
20479  * 
20480  * 
20481  * @constructor
20482  * Create a new Progress
20483  * @param {Object} config The config object
20484  */
20485
20486 Roo.bootstrap.Progress = function(config){
20487     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20488 };
20489
20490 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20491     
20492     striped : false,
20493     active: false,
20494     
20495     getAutoCreate : function(){
20496         var cfg = {
20497             tag: 'div',
20498             cls: 'progress'
20499         };
20500         
20501         
20502         if(this.striped){
20503             cfg.cls += ' progress-striped';
20504         }
20505       
20506         if(this.active){
20507             cfg.cls += ' active';
20508         }
20509         
20510         
20511         return cfg;
20512     }
20513    
20514 });
20515
20516  
20517
20518  /*
20519  * - LGPL
20520  *
20521  * ProgressBar
20522  * 
20523  */
20524
20525 /**
20526  * @class Roo.bootstrap.ProgressBar
20527  * @extends Roo.bootstrap.Component
20528  * Bootstrap ProgressBar class
20529  * @cfg {Number} aria_valuenow aria-value now
20530  * @cfg {Number} aria_valuemin aria-value min
20531  * @cfg {Number} aria_valuemax aria-value max
20532  * @cfg {String} label label for the progress bar
20533  * @cfg {String} panel (success | info | warning | danger )
20534  * @cfg {String} role role of the progress bar
20535  * @cfg {String} sr_only text
20536  * 
20537  * 
20538  * @constructor
20539  * Create a new ProgressBar
20540  * @param {Object} config The config object
20541  */
20542
20543 Roo.bootstrap.ProgressBar = function(config){
20544     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20545 };
20546
20547 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20548     
20549     aria_valuenow : 0,
20550     aria_valuemin : 0,
20551     aria_valuemax : 100,
20552     label : false,
20553     panel : false,
20554     role : false,
20555     sr_only: false,
20556     
20557     getAutoCreate : function()
20558     {
20559         
20560         var cfg = {
20561             tag: 'div',
20562             cls: 'progress-bar',
20563             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20564         };
20565         
20566         if(this.sr_only){
20567             cfg.cn = {
20568                 tag: 'span',
20569                 cls: 'sr-only',
20570                 html: this.sr_only
20571             }
20572         }
20573         
20574         if(this.role){
20575             cfg.role = this.role;
20576         }
20577         
20578         if(this.aria_valuenow){
20579             cfg['aria-valuenow'] = this.aria_valuenow;
20580         }
20581         
20582         if(this.aria_valuemin){
20583             cfg['aria-valuemin'] = this.aria_valuemin;
20584         }
20585         
20586         if(this.aria_valuemax){
20587             cfg['aria-valuemax'] = this.aria_valuemax;
20588         }
20589         
20590         if(this.label && !this.sr_only){
20591             cfg.html = this.label;
20592         }
20593         
20594         if(this.panel){
20595             cfg.cls += ' progress-bar-' + this.panel;
20596         }
20597         
20598         return cfg;
20599     },
20600     
20601     update : function(aria_valuenow)
20602     {
20603         this.aria_valuenow = aria_valuenow;
20604         
20605         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20606     }
20607    
20608 });
20609
20610  
20611
20612  /*
20613  * - LGPL
20614  *
20615  * column
20616  * 
20617  */
20618
20619 /**
20620  * @class Roo.bootstrap.TabGroup
20621  * @extends Roo.bootstrap.Column
20622  * Bootstrap Column class
20623  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20624  * @cfg {Boolean} carousel true to make the group behave like a carousel
20625  * @cfg {Boolean} bullets show bullets for the panels
20626  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20627  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20628  * @cfg {Boolean} showarrow (true|false) show arrow default true
20629  * 
20630  * @constructor
20631  * Create a new TabGroup
20632  * @param {Object} config The config object
20633  */
20634
20635 Roo.bootstrap.TabGroup = function(config){
20636     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20637     if (!this.navId) {
20638         this.navId = Roo.id();
20639     }
20640     this.tabs = [];
20641     Roo.bootstrap.TabGroup.register(this);
20642     
20643 };
20644
20645 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20646     
20647     carousel : false,
20648     transition : false,
20649     bullets : 0,
20650     timer : 0,
20651     autoslide : false,
20652     slideFn : false,
20653     slideOnTouch : false,
20654     showarrow : true,
20655     
20656     getAutoCreate : function()
20657     {
20658         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20659         
20660         cfg.cls += ' tab-content';
20661         
20662         if (this.carousel) {
20663             cfg.cls += ' carousel slide';
20664             
20665             cfg.cn = [{
20666                cls : 'carousel-inner',
20667                cn : []
20668             }];
20669         
20670             if(this.bullets  && !Roo.isTouch){
20671                 
20672                 var bullets = {
20673                     cls : 'carousel-bullets',
20674                     cn : []
20675                 };
20676                
20677                 if(this.bullets_cls){
20678                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20679                 }
20680                 
20681                 bullets.cn.push({
20682                     cls : 'clear'
20683                 });
20684                 
20685                 cfg.cn[0].cn.push(bullets);
20686             }
20687             
20688             if(this.showarrow){
20689                 cfg.cn[0].cn.push({
20690                     tag : 'div',
20691                     class : 'carousel-arrow',
20692                     cn : [
20693                         {
20694                             tag : 'div',
20695                             class : 'carousel-prev',
20696                             cn : [
20697                                 {
20698                                     tag : 'i',
20699                                     class : 'fa fa-chevron-left'
20700                                 }
20701                             ]
20702                         },
20703                         {
20704                             tag : 'div',
20705                             class : 'carousel-next',
20706                             cn : [
20707                                 {
20708                                     tag : 'i',
20709                                     class : 'fa fa-chevron-right'
20710                                 }
20711                             ]
20712                         }
20713                     ]
20714                 });
20715             }
20716             
20717         }
20718         
20719         return cfg;
20720     },
20721     
20722     initEvents:  function()
20723     {
20724 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20725 //            this.el.on("touchstart", this.onTouchStart, this);
20726 //        }
20727         
20728         if(this.autoslide){
20729             var _this = this;
20730             
20731             this.slideFn = window.setInterval(function() {
20732                 _this.showPanelNext();
20733             }, this.timer);
20734         }
20735         
20736         if(this.showarrow){
20737             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20738             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20739         }
20740         
20741         
20742     },
20743     
20744 //    onTouchStart : function(e, el, o)
20745 //    {
20746 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20747 //            return;
20748 //        }
20749 //        
20750 //        this.showPanelNext();
20751 //    },
20752     
20753     
20754     getChildContainer : function()
20755     {
20756         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20757     },
20758     
20759     /**
20760     * register a Navigation item
20761     * @param {Roo.bootstrap.NavItem} the navitem to add
20762     */
20763     register : function(item)
20764     {
20765         this.tabs.push( item);
20766         item.navId = this.navId; // not really needed..
20767         this.addBullet();
20768     
20769     },
20770     
20771     getActivePanel : function()
20772     {
20773         var r = false;
20774         Roo.each(this.tabs, function(t) {
20775             if (t.active) {
20776                 r = t;
20777                 return false;
20778             }
20779             return null;
20780         });
20781         return r;
20782         
20783     },
20784     getPanelByName : function(n)
20785     {
20786         var r = false;
20787         Roo.each(this.tabs, function(t) {
20788             if (t.tabId == n) {
20789                 r = t;
20790                 return false;
20791             }
20792             return null;
20793         });
20794         return r;
20795     },
20796     indexOfPanel : function(p)
20797     {
20798         var r = false;
20799         Roo.each(this.tabs, function(t,i) {
20800             if (t.tabId == p.tabId) {
20801                 r = i;
20802                 return false;
20803             }
20804             return null;
20805         });
20806         return r;
20807     },
20808     /**
20809      * show a specific panel
20810      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20811      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20812      */
20813     showPanel : function (pan)
20814     {
20815         if(this.transition || typeof(pan) == 'undefined'){
20816             Roo.log("waiting for the transitionend");
20817             return false;
20818         }
20819         
20820         if (typeof(pan) == 'number') {
20821             pan = this.tabs[pan];
20822         }
20823         
20824         if (typeof(pan) == 'string') {
20825             pan = this.getPanelByName(pan);
20826         }
20827         
20828         var cur = this.getActivePanel();
20829         
20830         if(!pan || !cur){
20831             Roo.log('pan or acitve pan is undefined');
20832             return false;
20833         }
20834         
20835         if (pan.tabId == this.getActivePanel().tabId) {
20836             return true;
20837         }
20838         
20839         if (false === cur.fireEvent('beforedeactivate')) {
20840             return false;
20841         }
20842         
20843         if(this.bullets > 0 && !Roo.isTouch){
20844             this.setActiveBullet(this.indexOfPanel(pan));
20845         }
20846         
20847         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20848             
20849             //class="carousel-item carousel-item-next carousel-item-left"
20850             
20851             this.transition = true;
20852             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20853             var lr = dir == 'next' ? 'left' : 'right';
20854             pan.el.addClass(dir); // or prev
20855             pan.el.addClass('carousel-item-' + dir); // or prev
20856             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20857             cur.el.addClass(lr); // or right
20858             pan.el.addClass(lr);
20859             cur.el.addClass('carousel-item-' +lr); // or right
20860             pan.el.addClass('carousel-item-' +lr);
20861             
20862             
20863             var _this = this;
20864             cur.el.on('transitionend', function() {
20865                 Roo.log("trans end?");
20866                 
20867                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20868                 pan.setActive(true);
20869                 
20870                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20871                 cur.setActive(false);
20872                 
20873                 _this.transition = false;
20874                 
20875             }, this, { single:  true } );
20876             
20877             return true;
20878         }
20879         
20880         cur.setActive(false);
20881         pan.setActive(true);
20882         
20883         return true;
20884         
20885     },
20886     showPanelNext : function()
20887     {
20888         var i = this.indexOfPanel(this.getActivePanel());
20889         
20890         if (i >= this.tabs.length - 1 && !this.autoslide) {
20891             return;
20892         }
20893         
20894         if (i >= this.tabs.length - 1 && this.autoslide) {
20895             i = -1;
20896         }
20897         
20898         this.showPanel(this.tabs[i+1]);
20899     },
20900     
20901     showPanelPrev : function()
20902     {
20903         var i = this.indexOfPanel(this.getActivePanel());
20904         
20905         if (i  < 1 && !this.autoslide) {
20906             return;
20907         }
20908         
20909         if (i < 1 && this.autoslide) {
20910             i = this.tabs.length;
20911         }
20912         
20913         this.showPanel(this.tabs[i-1]);
20914     },
20915     
20916     
20917     addBullet: function()
20918     {
20919         if(!this.bullets || Roo.isTouch){
20920             return;
20921         }
20922         var ctr = this.el.select('.carousel-bullets',true).first();
20923         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20924         var bullet = ctr.createChild({
20925             cls : 'bullet bullet-' + i
20926         },ctr.dom.lastChild);
20927         
20928         
20929         var _this = this;
20930         
20931         bullet.on('click', (function(e, el, o, ii, t){
20932
20933             e.preventDefault();
20934
20935             this.showPanel(ii);
20936
20937             if(this.autoslide && this.slideFn){
20938                 clearInterval(this.slideFn);
20939                 this.slideFn = window.setInterval(function() {
20940                     _this.showPanelNext();
20941                 }, this.timer);
20942             }
20943
20944         }).createDelegate(this, [i, bullet], true));
20945                 
20946         
20947     },
20948      
20949     setActiveBullet : function(i)
20950     {
20951         if(Roo.isTouch){
20952             return;
20953         }
20954         
20955         Roo.each(this.el.select('.bullet', true).elements, function(el){
20956             el.removeClass('selected');
20957         });
20958
20959         var bullet = this.el.select('.bullet-' + i, true).first();
20960         
20961         if(!bullet){
20962             return;
20963         }
20964         
20965         bullet.addClass('selected');
20966     }
20967     
20968     
20969   
20970 });
20971
20972  
20973
20974  
20975  
20976 Roo.apply(Roo.bootstrap.TabGroup, {
20977     
20978     groups: {},
20979      /**
20980     * register a Navigation Group
20981     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20982     */
20983     register : function(navgrp)
20984     {
20985         this.groups[navgrp.navId] = navgrp;
20986         
20987     },
20988     /**
20989     * fetch a Navigation Group based on the navigation ID
20990     * if one does not exist , it will get created.
20991     * @param {string} the navgroup to add
20992     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20993     */
20994     get: function(navId) {
20995         if (typeof(this.groups[navId]) == 'undefined') {
20996             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20997         }
20998         return this.groups[navId] ;
20999     }
21000     
21001     
21002     
21003 });
21004
21005  /*
21006  * - LGPL
21007  *
21008  * TabPanel
21009  * 
21010  */
21011
21012 /**
21013  * @class Roo.bootstrap.TabPanel
21014  * @extends Roo.bootstrap.Component
21015  * Bootstrap TabPanel class
21016  * @cfg {Boolean} active panel active
21017  * @cfg {String} html panel content
21018  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21019  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21020  * @cfg {String} href click to link..
21021  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21022  * 
21023  * 
21024  * @constructor
21025  * Create a new TabPanel
21026  * @param {Object} config The config object
21027  */
21028
21029 Roo.bootstrap.TabPanel = function(config){
21030     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21031     this.addEvents({
21032         /**
21033              * @event changed
21034              * Fires when the active status changes
21035              * @param {Roo.bootstrap.TabPanel} this
21036              * @param {Boolean} state the new state
21037             
21038          */
21039         'changed': true,
21040         /**
21041              * @event beforedeactivate
21042              * Fires before a tab is de-activated - can be used to do validation on a form.
21043              * @param {Roo.bootstrap.TabPanel} this
21044              * @return {Boolean} false if there is an error
21045             
21046          */
21047         'beforedeactivate': true
21048      });
21049     
21050     this.tabId = this.tabId || Roo.id();
21051   
21052 };
21053
21054 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21055     
21056     active: false,
21057     html: false,
21058     tabId: false,
21059     navId : false,
21060     href : '',
21061     touchSlide : false,
21062     getAutoCreate : function(){
21063         
21064         
21065         var cfg = {
21066             tag: 'div',
21067             // item is needed for carousel - not sure if it has any effect otherwise
21068             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21069             html: this.html || ''
21070         };
21071         
21072         if(this.active){
21073             cfg.cls += ' active';
21074         }
21075         
21076         if(this.tabId){
21077             cfg.tabId = this.tabId;
21078         }
21079         
21080         
21081         
21082         return cfg;
21083     },
21084     
21085     initEvents:  function()
21086     {
21087         var p = this.parent();
21088         
21089         this.navId = this.navId || p.navId;
21090         
21091         if (typeof(this.navId) != 'undefined') {
21092             // not really needed.. but just in case.. parent should be a NavGroup.
21093             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21094             
21095             tg.register(this);
21096             
21097             var i = tg.tabs.length - 1;
21098             
21099             if(this.active && tg.bullets > 0 && i < tg.bullets){
21100                 tg.setActiveBullet(i);
21101             }
21102         }
21103         
21104         this.el.on('click', this.onClick, this);
21105         
21106         if(Roo.isTouch && this.touchSlide){
21107             this.el.on("touchstart", this.onTouchStart, this);
21108             this.el.on("touchmove", this.onTouchMove, this);
21109             this.el.on("touchend", this.onTouchEnd, this);
21110         }
21111         
21112     },
21113     
21114     onRender : function(ct, position)
21115     {
21116         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21117     },
21118     
21119     setActive : function(state)
21120     {
21121         Roo.log("panel - set active " + this.tabId + "=" + state);
21122         
21123         this.active = state;
21124         if (!state) {
21125             this.el.removeClass('active');
21126             
21127         } else  if (!this.el.hasClass('active')) {
21128             this.el.addClass('active');
21129         }
21130         
21131         this.fireEvent('changed', this, state);
21132     },
21133     
21134     onClick : function(e)
21135     {
21136         e.preventDefault();
21137         
21138         if(!this.href.length){
21139             return;
21140         }
21141         
21142         window.location.href = this.href;
21143     },
21144     
21145     startX : 0,
21146     startY : 0,
21147     endX : 0,
21148     endY : 0,
21149     swiping : false,
21150     
21151     onTouchStart : function(e)
21152     {
21153         this.swiping = false;
21154         
21155         this.startX = e.browserEvent.touches[0].clientX;
21156         this.startY = e.browserEvent.touches[0].clientY;
21157     },
21158     
21159     onTouchMove : function(e)
21160     {
21161         this.swiping = true;
21162         
21163         this.endX = e.browserEvent.touches[0].clientX;
21164         this.endY = e.browserEvent.touches[0].clientY;
21165     },
21166     
21167     onTouchEnd : function(e)
21168     {
21169         if(!this.swiping){
21170             this.onClick(e);
21171             return;
21172         }
21173         
21174         var tabGroup = this.parent();
21175         
21176         if(this.endX > this.startX){ // swiping right
21177             tabGroup.showPanelPrev();
21178             return;
21179         }
21180         
21181         if(this.startX > this.endX){ // swiping left
21182             tabGroup.showPanelNext();
21183             return;
21184         }
21185     }
21186     
21187     
21188 });
21189  
21190
21191  
21192
21193  /*
21194  * - LGPL
21195  *
21196  * DateField
21197  * 
21198  */
21199
21200 /**
21201  * @class Roo.bootstrap.DateField
21202  * @extends Roo.bootstrap.Input
21203  * Bootstrap DateField class
21204  * @cfg {Number} weekStart default 0
21205  * @cfg {String} viewMode default empty, (months|years)
21206  * @cfg {String} minViewMode default empty, (months|years)
21207  * @cfg {Number} startDate default -Infinity
21208  * @cfg {Number} endDate default Infinity
21209  * @cfg {Boolean} todayHighlight default false
21210  * @cfg {Boolean} todayBtn default false
21211  * @cfg {Boolean} calendarWeeks default false
21212  * @cfg {Object} daysOfWeekDisabled default empty
21213  * @cfg {Boolean} singleMode default false (true | false)
21214  * 
21215  * @cfg {Boolean} keyboardNavigation default true
21216  * @cfg {String} language default en
21217  * 
21218  * @constructor
21219  * Create a new DateField
21220  * @param {Object} config The config object
21221  */
21222
21223 Roo.bootstrap.DateField = function(config){
21224     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21225      this.addEvents({
21226             /**
21227              * @event show
21228              * Fires when this field show.
21229              * @param {Roo.bootstrap.DateField} this
21230              * @param {Mixed} date The date value
21231              */
21232             show : true,
21233             /**
21234              * @event show
21235              * Fires when this field hide.
21236              * @param {Roo.bootstrap.DateField} this
21237              * @param {Mixed} date The date value
21238              */
21239             hide : true,
21240             /**
21241              * @event select
21242              * Fires when select a date.
21243              * @param {Roo.bootstrap.DateField} this
21244              * @param {Mixed} date The date value
21245              */
21246             select : true,
21247             /**
21248              * @event beforeselect
21249              * Fires when before select a date.
21250              * @param {Roo.bootstrap.DateField} this
21251              * @param {Mixed} date The date value
21252              */
21253             beforeselect : true
21254         });
21255 };
21256
21257 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21258     
21259     /**
21260      * @cfg {String} format
21261      * The default date format string which can be overriden for localization support.  The format must be
21262      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21263      */
21264     format : "m/d/y",
21265     /**
21266      * @cfg {String} altFormats
21267      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21268      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21269      */
21270     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21271     
21272     weekStart : 0,
21273     
21274     viewMode : '',
21275     
21276     minViewMode : '',
21277     
21278     todayHighlight : false,
21279     
21280     todayBtn: false,
21281     
21282     language: 'en',
21283     
21284     keyboardNavigation: true,
21285     
21286     calendarWeeks: false,
21287     
21288     startDate: -Infinity,
21289     
21290     endDate: Infinity,
21291     
21292     daysOfWeekDisabled: [],
21293     
21294     _events: [],
21295     
21296     singleMode : false,
21297     
21298     UTCDate: function()
21299     {
21300         return new Date(Date.UTC.apply(Date, arguments));
21301     },
21302     
21303     UTCToday: function()
21304     {
21305         var today = new Date();
21306         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21307     },
21308     
21309     getDate: function() {
21310             var d = this.getUTCDate();
21311             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21312     },
21313     
21314     getUTCDate: function() {
21315             return this.date;
21316     },
21317     
21318     setDate: function(d) {
21319             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21320     },
21321     
21322     setUTCDate: function(d) {
21323             this.date = d;
21324             this.setValue(this.formatDate(this.date));
21325     },
21326         
21327     onRender: function(ct, position)
21328     {
21329         
21330         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21331         
21332         this.language = this.language || 'en';
21333         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21334         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21335         
21336         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21337         this.format = this.format || 'm/d/y';
21338         this.isInline = false;
21339         this.isInput = true;
21340         this.component = this.el.select('.add-on', true).first() || false;
21341         this.component = (this.component && this.component.length === 0) ? false : this.component;
21342         this.hasInput = this.component && this.inputEl().length;
21343         
21344         if (typeof(this.minViewMode === 'string')) {
21345             switch (this.minViewMode) {
21346                 case 'months':
21347                     this.minViewMode = 1;
21348                     break;
21349                 case 'years':
21350                     this.minViewMode = 2;
21351                     break;
21352                 default:
21353                     this.minViewMode = 0;
21354                     break;
21355             }
21356         }
21357         
21358         if (typeof(this.viewMode === 'string')) {
21359             switch (this.viewMode) {
21360                 case 'months':
21361                     this.viewMode = 1;
21362                     break;
21363                 case 'years':
21364                     this.viewMode = 2;
21365                     break;
21366                 default:
21367                     this.viewMode = 0;
21368                     break;
21369             }
21370         }
21371                 
21372         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21373         
21374 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21375         
21376         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21377         
21378         this.picker().on('mousedown', this.onMousedown, this);
21379         this.picker().on('click', this.onClick, this);
21380         
21381         this.picker().addClass('datepicker-dropdown');
21382         
21383         this.startViewMode = this.viewMode;
21384         
21385         if(this.singleMode){
21386             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21387                 v.setVisibilityMode(Roo.Element.DISPLAY);
21388                 v.hide();
21389             });
21390             
21391             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21392                 v.setStyle('width', '189px');
21393             });
21394         }
21395         
21396         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21397             if(!this.calendarWeeks){
21398                 v.remove();
21399                 return;
21400             }
21401             
21402             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21403             v.attr('colspan', function(i, val){
21404                 return parseInt(val) + 1;
21405             });
21406         });
21407                         
21408         
21409         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21410         
21411         this.setStartDate(this.startDate);
21412         this.setEndDate(this.endDate);
21413         
21414         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21415         
21416         this.fillDow();
21417         this.fillMonths();
21418         this.update();
21419         this.showMode();
21420         
21421         if(this.isInline) {
21422             this.showPopup();
21423         }
21424     },
21425     
21426     picker : function()
21427     {
21428         return this.pickerEl;
21429 //        return this.el.select('.datepicker', true).first();
21430     },
21431     
21432     fillDow: function()
21433     {
21434         var dowCnt = this.weekStart;
21435         
21436         var dow = {
21437             tag: 'tr',
21438             cn: [
21439                 
21440             ]
21441         };
21442         
21443         if(this.calendarWeeks){
21444             dow.cn.push({
21445                 tag: 'th',
21446                 cls: 'cw',
21447                 html: '&nbsp;'
21448             })
21449         }
21450         
21451         while (dowCnt < this.weekStart + 7) {
21452             dow.cn.push({
21453                 tag: 'th',
21454                 cls: 'dow',
21455                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21456             });
21457         }
21458         
21459         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21460     },
21461     
21462     fillMonths: function()
21463     {    
21464         var i = 0;
21465         var months = this.picker().select('>.datepicker-months td', true).first();
21466         
21467         months.dom.innerHTML = '';
21468         
21469         while (i < 12) {
21470             var month = {
21471                 tag: 'span',
21472                 cls: 'month',
21473                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21474             };
21475             
21476             months.createChild(month);
21477         }
21478         
21479     },
21480     
21481     update: function()
21482     {
21483         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;
21484         
21485         if (this.date < this.startDate) {
21486             this.viewDate = new Date(this.startDate);
21487         } else if (this.date > this.endDate) {
21488             this.viewDate = new Date(this.endDate);
21489         } else {
21490             this.viewDate = new Date(this.date);
21491         }
21492         
21493         this.fill();
21494     },
21495     
21496     fill: function() 
21497     {
21498         var d = new Date(this.viewDate),
21499                 year = d.getUTCFullYear(),
21500                 month = d.getUTCMonth(),
21501                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21502                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21503                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21504                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21505                 currentDate = this.date && this.date.valueOf(),
21506                 today = this.UTCToday();
21507         
21508         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21509         
21510 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21511         
21512 //        this.picker.select('>tfoot th.today').
21513 //                                              .text(dates[this.language].today)
21514 //                                              .toggle(this.todayBtn !== false);
21515     
21516         this.updateNavArrows();
21517         this.fillMonths();
21518                                                 
21519         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21520         
21521         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21522          
21523         prevMonth.setUTCDate(day);
21524         
21525         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21526         
21527         var nextMonth = new Date(prevMonth);
21528         
21529         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21530         
21531         nextMonth = nextMonth.valueOf();
21532         
21533         var fillMonths = false;
21534         
21535         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21536         
21537         while(prevMonth.valueOf() <= nextMonth) {
21538             var clsName = '';
21539             
21540             if (prevMonth.getUTCDay() === this.weekStart) {
21541                 if(fillMonths){
21542                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21543                 }
21544                     
21545                 fillMonths = {
21546                     tag: 'tr',
21547                     cn: []
21548                 };
21549                 
21550                 if(this.calendarWeeks){
21551                     // ISO 8601: First week contains first thursday.
21552                     // ISO also states week starts on Monday, but we can be more abstract here.
21553                     var
21554                     // Start of current week: based on weekstart/current date
21555                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21556                     // Thursday of this week
21557                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21558                     // First Thursday of year, year from thursday
21559                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21560                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21561                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21562                     
21563                     fillMonths.cn.push({
21564                         tag: 'td',
21565                         cls: 'cw',
21566                         html: calWeek
21567                     });
21568                 }
21569             }
21570             
21571             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21572                 clsName += ' old';
21573             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21574                 clsName += ' new';
21575             }
21576             if (this.todayHighlight &&
21577                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21578                 prevMonth.getUTCMonth() == today.getMonth() &&
21579                 prevMonth.getUTCDate() == today.getDate()) {
21580                 clsName += ' today';
21581             }
21582             
21583             if (currentDate && prevMonth.valueOf() === currentDate) {
21584                 clsName += ' active';
21585             }
21586             
21587             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21588                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21589                     clsName += ' disabled';
21590             }
21591             
21592             fillMonths.cn.push({
21593                 tag: 'td',
21594                 cls: 'day ' + clsName,
21595                 html: prevMonth.getDate()
21596             });
21597             
21598             prevMonth.setDate(prevMonth.getDate()+1);
21599         }
21600           
21601         var currentYear = this.date && this.date.getUTCFullYear();
21602         var currentMonth = this.date && this.date.getUTCMonth();
21603         
21604         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21605         
21606         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21607             v.removeClass('active');
21608             
21609             if(currentYear === year && k === currentMonth){
21610                 v.addClass('active');
21611             }
21612             
21613             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21614                 v.addClass('disabled');
21615             }
21616             
21617         });
21618         
21619         
21620         year = parseInt(year/10, 10) * 10;
21621         
21622         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21623         
21624         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21625         
21626         year -= 1;
21627         for (var i = -1; i < 11; i++) {
21628             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21629                 tag: 'span',
21630                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21631                 html: year
21632             });
21633             
21634             year += 1;
21635         }
21636     },
21637     
21638     showMode: function(dir) 
21639     {
21640         if (dir) {
21641             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21642         }
21643         
21644         Roo.each(this.picker().select('>div',true).elements, function(v){
21645             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21646             v.hide();
21647         });
21648         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21649     },
21650     
21651     place: function()
21652     {
21653         if(this.isInline) {
21654             return;
21655         }
21656         
21657         this.picker().removeClass(['bottom', 'top']);
21658         
21659         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21660             /*
21661              * place to the top of element!
21662              *
21663              */
21664             
21665             this.picker().addClass('top');
21666             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21667             
21668             return;
21669         }
21670         
21671         this.picker().addClass('bottom');
21672         
21673         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21674     },
21675     
21676     parseDate : function(value)
21677     {
21678         if(!value || value instanceof Date){
21679             return value;
21680         }
21681         var v = Date.parseDate(value, this.format);
21682         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21683             v = Date.parseDate(value, 'Y-m-d');
21684         }
21685         if(!v && this.altFormats){
21686             if(!this.altFormatsArray){
21687                 this.altFormatsArray = this.altFormats.split("|");
21688             }
21689             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21690                 v = Date.parseDate(value, this.altFormatsArray[i]);
21691             }
21692         }
21693         return v;
21694     },
21695     
21696     formatDate : function(date, fmt)
21697     {   
21698         return (!date || !(date instanceof Date)) ?
21699         date : date.dateFormat(fmt || this.format);
21700     },
21701     
21702     onFocus : function()
21703     {
21704         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21705         this.showPopup();
21706     },
21707     
21708     onBlur : function()
21709     {
21710         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21711         
21712         var d = this.inputEl().getValue();
21713         
21714         this.setValue(d);
21715                 
21716         this.hidePopup();
21717     },
21718     
21719     showPopup : function()
21720     {
21721         this.picker().show();
21722         this.update();
21723         this.place();
21724         
21725         this.fireEvent('showpopup', this, this.date);
21726     },
21727     
21728     hidePopup : function()
21729     {
21730         if(this.isInline) {
21731             return;
21732         }
21733         this.picker().hide();
21734         this.viewMode = this.startViewMode;
21735         this.showMode();
21736         
21737         this.fireEvent('hidepopup', this, this.date);
21738         
21739     },
21740     
21741     onMousedown: function(e)
21742     {
21743         e.stopPropagation();
21744         e.preventDefault();
21745     },
21746     
21747     keyup: function(e)
21748     {
21749         Roo.bootstrap.DateField.superclass.keyup.call(this);
21750         this.update();
21751     },
21752
21753     setValue: function(v)
21754     {
21755         if(this.fireEvent('beforeselect', this, v) !== false){
21756             var d = new Date(this.parseDate(v) ).clearTime();
21757         
21758             if(isNaN(d.getTime())){
21759                 this.date = this.viewDate = '';
21760                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21761                 return;
21762             }
21763
21764             v = this.formatDate(d);
21765
21766             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21767
21768             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21769
21770             this.update();
21771
21772             this.fireEvent('select', this, this.date);
21773         }
21774     },
21775     
21776     getValue: function()
21777     {
21778         return this.formatDate(this.date);
21779     },
21780     
21781     fireKey: function(e)
21782     {
21783         if (!this.picker().isVisible()){
21784             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21785                 this.showPopup();
21786             }
21787             return;
21788         }
21789         
21790         var dateChanged = false,
21791         dir, day, month,
21792         newDate, newViewDate;
21793         
21794         switch(e.keyCode){
21795             case 27: // escape
21796                 this.hidePopup();
21797                 e.preventDefault();
21798                 break;
21799             case 37: // left
21800             case 39: // right
21801                 if (!this.keyboardNavigation) {
21802                     break;
21803                 }
21804                 dir = e.keyCode == 37 ? -1 : 1;
21805                 
21806                 if (e.ctrlKey){
21807                     newDate = this.moveYear(this.date, dir);
21808                     newViewDate = this.moveYear(this.viewDate, dir);
21809                 } else if (e.shiftKey){
21810                     newDate = this.moveMonth(this.date, dir);
21811                     newViewDate = this.moveMonth(this.viewDate, dir);
21812                 } else {
21813                     newDate = new Date(this.date);
21814                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21815                     newViewDate = new Date(this.viewDate);
21816                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21817                 }
21818                 if (this.dateWithinRange(newDate)){
21819                     this.date = newDate;
21820                     this.viewDate = newViewDate;
21821                     this.setValue(this.formatDate(this.date));
21822 //                    this.update();
21823                     e.preventDefault();
21824                     dateChanged = true;
21825                 }
21826                 break;
21827             case 38: // up
21828             case 40: // down
21829                 if (!this.keyboardNavigation) {
21830                     break;
21831                 }
21832                 dir = e.keyCode == 38 ? -1 : 1;
21833                 if (e.ctrlKey){
21834                     newDate = this.moveYear(this.date, dir);
21835                     newViewDate = this.moveYear(this.viewDate, dir);
21836                 } else if (e.shiftKey){
21837                     newDate = this.moveMonth(this.date, dir);
21838                     newViewDate = this.moveMonth(this.viewDate, dir);
21839                 } else {
21840                     newDate = new Date(this.date);
21841                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21842                     newViewDate = new Date(this.viewDate);
21843                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21844                 }
21845                 if (this.dateWithinRange(newDate)){
21846                     this.date = newDate;
21847                     this.viewDate = newViewDate;
21848                     this.setValue(this.formatDate(this.date));
21849 //                    this.update();
21850                     e.preventDefault();
21851                     dateChanged = true;
21852                 }
21853                 break;
21854             case 13: // enter
21855                 this.setValue(this.formatDate(this.date));
21856                 this.hidePopup();
21857                 e.preventDefault();
21858                 break;
21859             case 9: // tab
21860                 this.setValue(this.formatDate(this.date));
21861                 this.hidePopup();
21862                 break;
21863             case 16: // shift
21864             case 17: // ctrl
21865             case 18: // alt
21866                 break;
21867             default :
21868                 this.hidePopup();
21869                 
21870         }
21871     },
21872     
21873     
21874     onClick: function(e) 
21875     {
21876         e.stopPropagation();
21877         e.preventDefault();
21878         
21879         var target = e.getTarget();
21880         
21881         if(target.nodeName.toLowerCase() === 'i'){
21882             target = Roo.get(target).dom.parentNode;
21883         }
21884         
21885         var nodeName = target.nodeName;
21886         var className = target.className;
21887         var html = target.innerHTML;
21888         //Roo.log(nodeName);
21889         
21890         switch(nodeName.toLowerCase()) {
21891             case 'th':
21892                 switch(className) {
21893                     case 'switch':
21894                         this.showMode(1);
21895                         break;
21896                     case 'prev':
21897                     case 'next':
21898                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21899                         switch(this.viewMode){
21900                                 case 0:
21901                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21902                                         break;
21903                                 case 1:
21904                                 case 2:
21905                                         this.viewDate = this.moveYear(this.viewDate, dir);
21906                                         break;
21907                         }
21908                         this.fill();
21909                         break;
21910                     case 'today':
21911                         var date = new Date();
21912                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21913 //                        this.fill()
21914                         this.setValue(this.formatDate(this.date));
21915                         
21916                         this.hidePopup();
21917                         break;
21918                 }
21919                 break;
21920             case 'span':
21921                 if (className.indexOf('disabled') < 0) {
21922                     this.viewDate.setUTCDate(1);
21923                     if (className.indexOf('month') > -1) {
21924                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21925                     } else {
21926                         var year = parseInt(html, 10) || 0;
21927                         this.viewDate.setUTCFullYear(year);
21928                         
21929                     }
21930                     
21931                     if(this.singleMode){
21932                         this.setValue(this.formatDate(this.viewDate));
21933                         this.hidePopup();
21934                         return;
21935                     }
21936                     
21937                     this.showMode(-1);
21938                     this.fill();
21939                 }
21940                 break;
21941                 
21942             case 'td':
21943                 //Roo.log(className);
21944                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21945                     var day = parseInt(html, 10) || 1;
21946                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21947                         month = (this.viewDate || new Date()).getUTCMonth();
21948
21949                     if (className.indexOf('old') > -1) {
21950                         if(month === 0 ){
21951                             month = 11;
21952                             year -= 1;
21953                         }else{
21954                             month -= 1;
21955                         }
21956                     } else if (className.indexOf('new') > -1) {
21957                         if (month == 11) {
21958                             month = 0;
21959                             year += 1;
21960                         } else {
21961                             month += 1;
21962                         }
21963                     }
21964                     //Roo.log([year,month,day]);
21965                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21966                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21967 //                    this.fill();
21968                     //Roo.log(this.formatDate(this.date));
21969                     this.setValue(this.formatDate(this.date));
21970                     this.hidePopup();
21971                 }
21972                 break;
21973         }
21974     },
21975     
21976     setStartDate: function(startDate)
21977     {
21978         this.startDate = startDate || -Infinity;
21979         if (this.startDate !== -Infinity) {
21980             this.startDate = this.parseDate(this.startDate);
21981         }
21982         this.update();
21983         this.updateNavArrows();
21984     },
21985
21986     setEndDate: function(endDate)
21987     {
21988         this.endDate = endDate || Infinity;
21989         if (this.endDate !== Infinity) {
21990             this.endDate = this.parseDate(this.endDate);
21991         }
21992         this.update();
21993         this.updateNavArrows();
21994     },
21995     
21996     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21997     {
21998         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21999         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22000             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22001         }
22002         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22003             return parseInt(d, 10);
22004         });
22005         this.update();
22006         this.updateNavArrows();
22007     },
22008     
22009     updateNavArrows: function() 
22010     {
22011         if(this.singleMode){
22012             return;
22013         }
22014         
22015         var d = new Date(this.viewDate),
22016         year = d.getUTCFullYear(),
22017         month = d.getUTCMonth();
22018         
22019         Roo.each(this.picker().select('.prev', true).elements, function(v){
22020             v.show();
22021             switch (this.viewMode) {
22022                 case 0:
22023
22024                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22025                         v.hide();
22026                     }
22027                     break;
22028                 case 1:
22029                 case 2:
22030                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22031                         v.hide();
22032                     }
22033                     break;
22034             }
22035         });
22036         
22037         Roo.each(this.picker().select('.next', true).elements, function(v){
22038             v.show();
22039             switch (this.viewMode) {
22040                 case 0:
22041
22042                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22043                         v.hide();
22044                     }
22045                     break;
22046                 case 1:
22047                 case 2:
22048                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22049                         v.hide();
22050                     }
22051                     break;
22052             }
22053         })
22054     },
22055     
22056     moveMonth: function(date, dir)
22057     {
22058         if (!dir) {
22059             return date;
22060         }
22061         var new_date = new Date(date.valueOf()),
22062         day = new_date.getUTCDate(),
22063         month = new_date.getUTCMonth(),
22064         mag = Math.abs(dir),
22065         new_month, test;
22066         dir = dir > 0 ? 1 : -1;
22067         if (mag == 1){
22068             test = dir == -1
22069             // If going back one month, make sure month is not current month
22070             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22071             ? function(){
22072                 return new_date.getUTCMonth() == month;
22073             }
22074             // If going forward one month, make sure month is as expected
22075             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22076             : function(){
22077                 return new_date.getUTCMonth() != new_month;
22078             };
22079             new_month = month + dir;
22080             new_date.setUTCMonth(new_month);
22081             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22082             if (new_month < 0 || new_month > 11) {
22083                 new_month = (new_month + 12) % 12;
22084             }
22085         } else {
22086             // For magnitudes >1, move one month at a time...
22087             for (var i=0; i<mag; i++) {
22088                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22089                 new_date = this.moveMonth(new_date, dir);
22090             }
22091             // ...then reset the day, keeping it in the new month
22092             new_month = new_date.getUTCMonth();
22093             new_date.setUTCDate(day);
22094             test = function(){
22095                 return new_month != new_date.getUTCMonth();
22096             };
22097         }
22098         // Common date-resetting loop -- if date is beyond end of month, make it
22099         // end of month
22100         while (test()){
22101             new_date.setUTCDate(--day);
22102             new_date.setUTCMonth(new_month);
22103         }
22104         return new_date;
22105     },
22106
22107     moveYear: function(date, dir)
22108     {
22109         return this.moveMonth(date, dir*12);
22110     },
22111
22112     dateWithinRange: function(date)
22113     {
22114         return date >= this.startDate && date <= this.endDate;
22115     },
22116
22117     
22118     remove: function() 
22119     {
22120         this.picker().remove();
22121     },
22122     
22123     validateValue : function(value)
22124     {
22125         if(this.getVisibilityEl().hasClass('hidden')){
22126             return true;
22127         }
22128         
22129         if(value.length < 1)  {
22130             if(this.allowBlank){
22131                 return true;
22132             }
22133             return false;
22134         }
22135         
22136         if(value.length < this.minLength){
22137             return false;
22138         }
22139         if(value.length > this.maxLength){
22140             return false;
22141         }
22142         if(this.vtype){
22143             var vt = Roo.form.VTypes;
22144             if(!vt[this.vtype](value, this)){
22145                 return false;
22146             }
22147         }
22148         if(typeof this.validator == "function"){
22149             var msg = this.validator(value);
22150             if(msg !== true){
22151                 return false;
22152             }
22153         }
22154         
22155         if(this.regex && !this.regex.test(value)){
22156             return false;
22157         }
22158         
22159         if(typeof(this.parseDate(value)) == 'undefined'){
22160             return false;
22161         }
22162         
22163         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22164             return false;
22165         }      
22166         
22167         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22168             return false;
22169         } 
22170         
22171         
22172         return true;
22173     },
22174     
22175     reset : function()
22176     {
22177         this.date = this.viewDate = '';
22178         
22179         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22180     }
22181    
22182 });
22183
22184 Roo.apply(Roo.bootstrap.DateField,  {
22185     
22186     head : {
22187         tag: 'thead',
22188         cn: [
22189         {
22190             tag: 'tr',
22191             cn: [
22192             {
22193                 tag: 'th',
22194                 cls: 'prev',
22195                 html: '<i class="fa fa-arrow-left"/>'
22196             },
22197             {
22198                 tag: 'th',
22199                 cls: 'switch',
22200                 colspan: '5'
22201             },
22202             {
22203                 tag: 'th',
22204                 cls: 'next',
22205                 html: '<i class="fa fa-arrow-right"/>'
22206             }
22207
22208             ]
22209         }
22210         ]
22211     },
22212     
22213     content : {
22214         tag: 'tbody',
22215         cn: [
22216         {
22217             tag: 'tr',
22218             cn: [
22219             {
22220                 tag: 'td',
22221                 colspan: '7'
22222             }
22223             ]
22224         }
22225         ]
22226     },
22227     
22228     footer : {
22229         tag: 'tfoot',
22230         cn: [
22231         {
22232             tag: 'tr',
22233             cn: [
22234             {
22235                 tag: 'th',
22236                 colspan: '7',
22237                 cls: 'today'
22238             }
22239                     
22240             ]
22241         }
22242         ]
22243     },
22244     
22245     dates:{
22246         en: {
22247             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22248             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22249             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22250             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22251             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22252             today: "Today"
22253         }
22254     },
22255     
22256     modes: [
22257     {
22258         clsName: 'days',
22259         navFnc: 'Month',
22260         navStep: 1
22261     },
22262     {
22263         clsName: 'months',
22264         navFnc: 'FullYear',
22265         navStep: 1
22266     },
22267     {
22268         clsName: 'years',
22269         navFnc: 'FullYear',
22270         navStep: 10
22271     }]
22272 });
22273
22274 Roo.apply(Roo.bootstrap.DateField,  {
22275   
22276     template : {
22277         tag: 'div',
22278         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22279         cn: [
22280         {
22281             tag: 'div',
22282             cls: 'datepicker-days',
22283             cn: [
22284             {
22285                 tag: 'table',
22286                 cls: 'table-condensed',
22287                 cn:[
22288                 Roo.bootstrap.DateField.head,
22289                 {
22290                     tag: 'tbody'
22291                 },
22292                 Roo.bootstrap.DateField.footer
22293                 ]
22294             }
22295             ]
22296         },
22297         {
22298             tag: 'div',
22299             cls: 'datepicker-months',
22300             cn: [
22301             {
22302                 tag: 'table',
22303                 cls: 'table-condensed',
22304                 cn:[
22305                 Roo.bootstrap.DateField.head,
22306                 Roo.bootstrap.DateField.content,
22307                 Roo.bootstrap.DateField.footer
22308                 ]
22309             }
22310             ]
22311         },
22312         {
22313             tag: 'div',
22314             cls: 'datepicker-years',
22315             cn: [
22316             {
22317                 tag: 'table',
22318                 cls: 'table-condensed',
22319                 cn:[
22320                 Roo.bootstrap.DateField.head,
22321                 Roo.bootstrap.DateField.content,
22322                 Roo.bootstrap.DateField.footer
22323                 ]
22324             }
22325             ]
22326         }
22327         ]
22328     }
22329 });
22330
22331  
22332
22333  /*
22334  * - LGPL
22335  *
22336  * TimeField
22337  * 
22338  */
22339
22340 /**
22341  * @class Roo.bootstrap.TimeField
22342  * @extends Roo.bootstrap.Input
22343  * Bootstrap DateField class
22344  * 
22345  * 
22346  * @constructor
22347  * Create a new TimeField
22348  * @param {Object} config The config object
22349  */
22350
22351 Roo.bootstrap.TimeField = function(config){
22352     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22353     this.addEvents({
22354             /**
22355              * @event show
22356              * Fires when this field show.
22357              * @param {Roo.bootstrap.DateField} thisthis
22358              * @param {Mixed} date The date value
22359              */
22360             show : true,
22361             /**
22362              * @event show
22363              * Fires when this field hide.
22364              * @param {Roo.bootstrap.DateField} this
22365              * @param {Mixed} date The date value
22366              */
22367             hide : true,
22368             /**
22369              * @event select
22370              * Fires when select a date.
22371              * @param {Roo.bootstrap.DateField} this
22372              * @param {Mixed} date The date value
22373              */
22374             select : true
22375         });
22376 };
22377
22378 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22379     
22380     /**
22381      * @cfg {String} format
22382      * The default time format string which can be overriden for localization support.  The format must be
22383      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22384      */
22385     format : "H:i",
22386
22387     getAutoCreate : function()
22388     {
22389         this.after = '<i class="fa far fa-clock"></i>';
22390         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22391         
22392          
22393     },
22394     onRender: function(ct, position)
22395     {
22396         
22397         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22398                 
22399         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22400         
22401         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22402         
22403         this.pop = this.picker().select('>.datepicker-time',true).first();
22404         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22405         
22406         this.picker().on('mousedown', this.onMousedown, this);
22407         this.picker().on('click', this.onClick, this);
22408         
22409         this.picker().addClass('datepicker-dropdown');
22410     
22411         this.fillTime();
22412         this.update();
22413             
22414         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22415         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22416         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22417         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22418         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22419         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22420
22421     },
22422     
22423     fireKey: function(e){
22424         if (!this.picker().isVisible()){
22425             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22426                 this.show();
22427             }
22428             return;
22429         }
22430
22431         e.preventDefault();
22432         
22433         switch(e.keyCode){
22434             case 27: // escape
22435                 this.hide();
22436                 break;
22437             case 37: // left
22438             case 39: // right
22439                 this.onTogglePeriod();
22440                 break;
22441             case 38: // up
22442                 this.onIncrementMinutes();
22443                 break;
22444             case 40: // down
22445                 this.onDecrementMinutes();
22446                 break;
22447             case 13: // enter
22448             case 9: // tab
22449                 this.setTime();
22450                 break;
22451         }
22452     },
22453     
22454     onClick: function(e) {
22455         e.stopPropagation();
22456         e.preventDefault();
22457     },
22458     
22459     picker : function()
22460     {
22461         return this.pickerEl;
22462     },
22463     
22464     fillTime: function()
22465     {    
22466         var time = this.pop.select('tbody', true).first();
22467         
22468         time.dom.innerHTML = '';
22469         
22470         time.createChild({
22471             tag: 'tr',
22472             cn: [
22473                 {
22474                     tag: 'td',
22475                     cn: [
22476                         {
22477                             tag: 'a',
22478                             href: '#',
22479                             cls: 'btn',
22480                             cn: [
22481                                 {
22482                                     tag: 'i',
22483                                     cls: 'hours-up fa fas fa-chevron-up'
22484                                 }
22485                             ]
22486                         } 
22487                     ]
22488                 },
22489                 {
22490                     tag: 'td',
22491                     cls: 'separator'
22492                 },
22493                 {
22494                     tag: 'td',
22495                     cn: [
22496                         {
22497                             tag: 'a',
22498                             href: '#',
22499                             cls: 'btn',
22500                             cn: [
22501                                 {
22502                                     tag: 'i',
22503                                     cls: 'minutes-up fa fas fa-chevron-up'
22504                                 }
22505                             ]
22506                         }
22507                     ]
22508                 },
22509                 {
22510                     tag: 'td',
22511                     cls: 'separator'
22512                 }
22513             ]
22514         });
22515         
22516         time.createChild({
22517             tag: 'tr',
22518             cn: [
22519                 {
22520                     tag: 'td',
22521                     cn: [
22522                         {
22523                             tag: 'span',
22524                             cls: 'timepicker-hour',
22525                             html: '00'
22526                         }  
22527                     ]
22528                 },
22529                 {
22530                     tag: 'td',
22531                     cls: 'separator',
22532                     html: ':'
22533                 },
22534                 {
22535                     tag: 'td',
22536                     cn: [
22537                         {
22538                             tag: 'span',
22539                             cls: 'timepicker-minute',
22540                             html: '00'
22541                         }  
22542                     ]
22543                 },
22544                 {
22545                     tag: 'td',
22546                     cls: 'separator'
22547                 },
22548                 {
22549                     tag: 'td',
22550                     cn: [
22551                         {
22552                             tag: 'button',
22553                             type: 'button',
22554                             cls: 'btn btn-primary period',
22555                             html: 'AM'
22556                             
22557                         }
22558                     ]
22559                 }
22560             ]
22561         });
22562         
22563         time.createChild({
22564             tag: 'tr',
22565             cn: [
22566                 {
22567                     tag: 'td',
22568                     cn: [
22569                         {
22570                             tag: 'a',
22571                             href: '#',
22572                             cls: 'btn',
22573                             cn: [
22574                                 {
22575                                     tag: 'span',
22576                                     cls: 'hours-down fa fas fa-chevron-down'
22577                                 }
22578                             ]
22579                         }
22580                     ]
22581                 },
22582                 {
22583                     tag: 'td',
22584                     cls: 'separator'
22585                 },
22586                 {
22587                     tag: 'td',
22588                     cn: [
22589                         {
22590                             tag: 'a',
22591                             href: '#',
22592                             cls: 'btn',
22593                             cn: [
22594                                 {
22595                                     tag: 'span',
22596                                     cls: 'minutes-down fa fas fa-chevron-down'
22597                                 }
22598                             ]
22599                         }
22600                     ]
22601                 },
22602                 {
22603                     tag: 'td',
22604                     cls: 'separator'
22605                 }
22606             ]
22607         });
22608         
22609     },
22610     
22611     update: function()
22612     {
22613         
22614         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22615         
22616         this.fill();
22617     },
22618     
22619     fill: function() 
22620     {
22621         var hours = this.time.getHours();
22622         var minutes = this.time.getMinutes();
22623         var period = 'AM';
22624         
22625         if(hours > 11){
22626             period = 'PM';
22627         }
22628         
22629         if(hours == 0){
22630             hours = 12;
22631         }
22632         
22633         
22634         if(hours > 12){
22635             hours = hours - 12;
22636         }
22637         
22638         if(hours < 10){
22639             hours = '0' + hours;
22640         }
22641         
22642         if(minutes < 10){
22643             minutes = '0' + minutes;
22644         }
22645         
22646         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22647         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22648         this.pop.select('button', true).first().dom.innerHTML = period;
22649         
22650     },
22651     
22652     place: function()
22653     {   
22654         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22655         
22656         var cls = ['bottom'];
22657         
22658         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22659             cls.pop();
22660             cls.push('top');
22661         }
22662         
22663         cls.push('right');
22664         
22665         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22666             cls.pop();
22667             cls.push('left');
22668         }
22669         //this.picker().setXY(20000,20000);
22670         this.picker().addClass(cls.join('-'));
22671         
22672         var _this = this;
22673         
22674         Roo.each(cls, function(c){
22675             if(c == 'bottom'){
22676                 (function() {
22677                  //  
22678                 }).defer(200);
22679                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22680                 //_this.picker().setTop(_this.inputEl().getHeight());
22681                 return;
22682             }
22683             if(c == 'top'){
22684                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22685                 
22686                 //_this.picker().setTop(0 - _this.picker().getHeight());
22687                 return;
22688             }
22689             /*
22690             if(c == 'left'){
22691                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22692                 return;
22693             }
22694             if(c == 'right'){
22695                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22696                 return;
22697             }
22698             */
22699         });
22700         
22701     },
22702   
22703     onFocus : function()
22704     {
22705         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22706         this.show();
22707     },
22708     
22709     onBlur : function()
22710     {
22711         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22712         this.hide();
22713     },
22714     
22715     show : function()
22716     {
22717         this.picker().show();
22718         this.pop.show();
22719         this.update();
22720         this.place();
22721         
22722         this.fireEvent('show', this, this.date);
22723     },
22724     
22725     hide : function()
22726     {
22727         this.picker().hide();
22728         this.pop.hide();
22729         
22730         this.fireEvent('hide', this, this.date);
22731     },
22732     
22733     setTime : function()
22734     {
22735         this.hide();
22736         this.setValue(this.time.format(this.format));
22737         
22738         this.fireEvent('select', this, this.date);
22739         
22740         
22741     },
22742     
22743     onMousedown: function(e){
22744         e.stopPropagation();
22745         e.preventDefault();
22746     },
22747     
22748     onIncrementHours: function()
22749     {
22750         Roo.log('onIncrementHours');
22751         this.time = this.time.add(Date.HOUR, 1);
22752         this.update();
22753         
22754     },
22755     
22756     onDecrementHours: function()
22757     {
22758         Roo.log('onDecrementHours');
22759         this.time = this.time.add(Date.HOUR, -1);
22760         this.update();
22761     },
22762     
22763     onIncrementMinutes: function()
22764     {
22765         Roo.log('onIncrementMinutes');
22766         this.time = this.time.add(Date.MINUTE, 1);
22767         this.update();
22768     },
22769     
22770     onDecrementMinutes: function()
22771     {
22772         Roo.log('onDecrementMinutes');
22773         this.time = this.time.add(Date.MINUTE, -1);
22774         this.update();
22775     },
22776     
22777     onTogglePeriod: function()
22778     {
22779         Roo.log('onTogglePeriod');
22780         this.time = this.time.add(Date.HOUR, 12);
22781         this.update();
22782     }
22783     
22784    
22785 });
22786  
22787
22788 Roo.apply(Roo.bootstrap.TimeField,  {
22789   
22790     template : {
22791         tag: 'div',
22792         cls: 'datepicker dropdown-menu',
22793         cn: [
22794             {
22795                 tag: 'div',
22796                 cls: 'datepicker-time',
22797                 cn: [
22798                 {
22799                     tag: 'table',
22800                     cls: 'table-condensed',
22801                     cn:[
22802                         {
22803                             tag: 'tbody',
22804                             cn: [
22805                                 {
22806                                     tag: 'tr',
22807                                     cn: [
22808                                     {
22809                                         tag: 'td',
22810                                         colspan: '7'
22811                                     }
22812                                     ]
22813                                 }
22814                             ]
22815                         },
22816                         {
22817                             tag: 'tfoot',
22818                             cn: [
22819                                 {
22820                                     tag: 'tr',
22821                                     cn: [
22822                                     {
22823                                         tag: 'th',
22824                                         colspan: '7',
22825                                         cls: '',
22826                                         cn: [
22827                                             {
22828                                                 tag: 'button',
22829                                                 cls: 'btn btn-info ok',
22830                                                 html: 'OK'
22831                                             }
22832                                         ]
22833                                     }
22834                     
22835                                     ]
22836                                 }
22837                             ]
22838                         }
22839                     ]
22840                 }
22841                 ]
22842             }
22843         ]
22844     }
22845 });
22846
22847  
22848
22849  /*
22850  * - LGPL
22851  *
22852  * MonthField
22853  * 
22854  */
22855
22856 /**
22857  * @class Roo.bootstrap.MonthField
22858  * @extends Roo.bootstrap.Input
22859  * Bootstrap MonthField class
22860  * 
22861  * @cfg {String} language default en
22862  * 
22863  * @constructor
22864  * Create a new MonthField
22865  * @param {Object} config The config object
22866  */
22867
22868 Roo.bootstrap.MonthField = function(config){
22869     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22870     
22871     this.addEvents({
22872         /**
22873          * @event show
22874          * Fires when this field show.
22875          * @param {Roo.bootstrap.MonthField} this
22876          * @param {Mixed} date The date value
22877          */
22878         show : true,
22879         /**
22880          * @event show
22881          * Fires when this field hide.
22882          * @param {Roo.bootstrap.MonthField} this
22883          * @param {Mixed} date The date value
22884          */
22885         hide : true,
22886         /**
22887          * @event select
22888          * Fires when select a date.
22889          * @param {Roo.bootstrap.MonthField} this
22890          * @param {String} oldvalue The old value
22891          * @param {String} newvalue The new value
22892          */
22893         select : true
22894     });
22895 };
22896
22897 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22898     
22899     onRender: function(ct, position)
22900     {
22901         
22902         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22903         
22904         this.language = this.language || 'en';
22905         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22906         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22907         
22908         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22909         this.isInline = false;
22910         this.isInput = true;
22911         this.component = this.el.select('.add-on', true).first() || false;
22912         this.component = (this.component && this.component.length === 0) ? false : this.component;
22913         this.hasInput = this.component && this.inputEL().length;
22914         
22915         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22916         
22917         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22918         
22919         this.picker().on('mousedown', this.onMousedown, this);
22920         this.picker().on('click', this.onClick, this);
22921         
22922         this.picker().addClass('datepicker-dropdown');
22923         
22924         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22925             v.setStyle('width', '189px');
22926         });
22927         
22928         this.fillMonths();
22929         
22930         this.update();
22931         
22932         if(this.isInline) {
22933             this.show();
22934         }
22935         
22936     },
22937     
22938     setValue: function(v, suppressEvent)
22939     {   
22940         var o = this.getValue();
22941         
22942         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22943         
22944         this.update();
22945
22946         if(suppressEvent !== true){
22947             this.fireEvent('select', this, o, v);
22948         }
22949         
22950     },
22951     
22952     getValue: function()
22953     {
22954         return this.value;
22955     },
22956     
22957     onClick: function(e) 
22958     {
22959         e.stopPropagation();
22960         e.preventDefault();
22961         
22962         var target = e.getTarget();
22963         
22964         if(target.nodeName.toLowerCase() === 'i'){
22965             target = Roo.get(target).dom.parentNode;
22966         }
22967         
22968         var nodeName = target.nodeName;
22969         var className = target.className;
22970         var html = target.innerHTML;
22971         
22972         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22973             return;
22974         }
22975         
22976         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22977         
22978         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22979         
22980         this.hide();
22981                         
22982     },
22983     
22984     picker : function()
22985     {
22986         return this.pickerEl;
22987     },
22988     
22989     fillMonths: function()
22990     {    
22991         var i = 0;
22992         var months = this.picker().select('>.datepicker-months td', true).first();
22993         
22994         months.dom.innerHTML = '';
22995         
22996         while (i < 12) {
22997             var month = {
22998                 tag: 'span',
22999                 cls: 'month',
23000                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23001             };
23002             
23003             months.createChild(month);
23004         }
23005         
23006     },
23007     
23008     update: function()
23009     {
23010         var _this = this;
23011         
23012         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23013             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23014         }
23015         
23016         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23017             e.removeClass('active');
23018             
23019             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23020                 e.addClass('active');
23021             }
23022         })
23023     },
23024     
23025     place: function()
23026     {
23027         if(this.isInline) {
23028             return;
23029         }
23030         
23031         this.picker().removeClass(['bottom', 'top']);
23032         
23033         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23034             /*
23035              * place to the top of element!
23036              *
23037              */
23038             
23039             this.picker().addClass('top');
23040             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23041             
23042             return;
23043         }
23044         
23045         this.picker().addClass('bottom');
23046         
23047         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23048     },
23049     
23050     onFocus : function()
23051     {
23052         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23053         this.show();
23054     },
23055     
23056     onBlur : function()
23057     {
23058         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23059         
23060         var d = this.inputEl().getValue();
23061         
23062         this.setValue(d);
23063                 
23064         this.hide();
23065     },
23066     
23067     show : function()
23068     {
23069         this.picker().show();
23070         this.picker().select('>.datepicker-months', true).first().show();
23071         this.update();
23072         this.place();
23073         
23074         this.fireEvent('show', this, this.date);
23075     },
23076     
23077     hide : function()
23078     {
23079         if(this.isInline) {
23080             return;
23081         }
23082         this.picker().hide();
23083         this.fireEvent('hide', this, this.date);
23084         
23085     },
23086     
23087     onMousedown: function(e)
23088     {
23089         e.stopPropagation();
23090         e.preventDefault();
23091     },
23092     
23093     keyup: function(e)
23094     {
23095         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23096         this.update();
23097     },
23098
23099     fireKey: function(e)
23100     {
23101         if (!this.picker().isVisible()){
23102             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23103                 this.show();
23104             }
23105             return;
23106         }
23107         
23108         var dir;
23109         
23110         switch(e.keyCode){
23111             case 27: // escape
23112                 this.hide();
23113                 e.preventDefault();
23114                 break;
23115             case 37: // left
23116             case 39: // right
23117                 dir = e.keyCode == 37 ? -1 : 1;
23118                 
23119                 this.vIndex = this.vIndex + dir;
23120                 
23121                 if(this.vIndex < 0){
23122                     this.vIndex = 0;
23123                 }
23124                 
23125                 if(this.vIndex > 11){
23126                     this.vIndex = 11;
23127                 }
23128                 
23129                 if(isNaN(this.vIndex)){
23130                     this.vIndex = 0;
23131                 }
23132                 
23133                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23134                 
23135                 break;
23136             case 38: // up
23137             case 40: // down
23138                 
23139                 dir = e.keyCode == 38 ? -1 : 1;
23140                 
23141                 this.vIndex = this.vIndex + dir * 4;
23142                 
23143                 if(this.vIndex < 0){
23144                     this.vIndex = 0;
23145                 }
23146                 
23147                 if(this.vIndex > 11){
23148                     this.vIndex = 11;
23149                 }
23150                 
23151                 if(isNaN(this.vIndex)){
23152                     this.vIndex = 0;
23153                 }
23154                 
23155                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23156                 break;
23157                 
23158             case 13: // enter
23159                 
23160                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23161                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23162                 }
23163                 
23164                 this.hide();
23165                 e.preventDefault();
23166                 break;
23167             case 9: // tab
23168                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23169                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23170                 }
23171                 this.hide();
23172                 break;
23173             case 16: // shift
23174             case 17: // ctrl
23175             case 18: // alt
23176                 break;
23177             default :
23178                 this.hide();
23179                 
23180         }
23181     },
23182     
23183     remove: function() 
23184     {
23185         this.picker().remove();
23186     }
23187    
23188 });
23189
23190 Roo.apply(Roo.bootstrap.MonthField,  {
23191     
23192     content : {
23193         tag: 'tbody',
23194         cn: [
23195         {
23196             tag: 'tr',
23197             cn: [
23198             {
23199                 tag: 'td',
23200                 colspan: '7'
23201             }
23202             ]
23203         }
23204         ]
23205     },
23206     
23207     dates:{
23208         en: {
23209             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23210             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23211         }
23212     }
23213 });
23214
23215 Roo.apply(Roo.bootstrap.MonthField,  {
23216   
23217     template : {
23218         tag: 'div',
23219         cls: 'datepicker dropdown-menu roo-dynamic',
23220         cn: [
23221             {
23222                 tag: 'div',
23223                 cls: 'datepicker-months',
23224                 cn: [
23225                 {
23226                     tag: 'table',
23227                     cls: 'table-condensed',
23228                     cn:[
23229                         Roo.bootstrap.DateField.content
23230                     ]
23231                 }
23232                 ]
23233             }
23234         ]
23235     }
23236 });
23237
23238  
23239
23240  
23241  /*
23242  * - LGPL
23243  *
23244  * CheckBox
23245  * 
23246  */
23247
23248 /**
23249  * @class Roo.bootstrap.CheckBox
23250  * @extends Roo.bootstrap.Input
23251  * Bootstrap CheckBox class
23252  * 
23253  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23254  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23255  * @cfg {String} boxLabel The text that appears beside the checkbox
23256  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23257  * @cfg {Boolean} checked initnal the element
23258  * @cfg {Boolean} inline inline the element (default false)
23259  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23260  * @cfg {String} tooltip label tooltip
23261  * 
23262  * @constructor
23263  * Create a new CheckBox
23264  * @param {Object} config The config object
23265  */
23266
23267 Roo.bootstrap.CheckBox = function(config){
23268     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23269    
23270     this.addEvents({
23271         /**
23272         * @event check
23273         * Fires when the element is checked or unchecked.
23274         * @param {Roo.bootstrap.CheckBox} this This input
23275         * @param {Boolean} checked The new checked value
23276         */
23277        check : true,
23278        /**
23279         * @event click
23280         * Fires when the element is click.
23281         * @param {Roo.bootstrap.CheckBox} this This input
23282         */
23283        click : true
23284     });
23285     
23286 };
23287
23288 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23289   
23290     inputType: 'checkbox',
23291     inputValue: 1,
23292     valueOff: 0,
23293     boxLabel: false,
23294     checked: false,
23295     weight : false,
23296     inline: false,
23297     tooltip : '',
23298     
23299     // checkbox success does not make any sense really.. 
23300     invalidClass : "",
23301     validClass : "",
23302     
23303     
23304     getAutoCreate : function()
23305     {
23306         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23307         
23308         var id = Roo.id();
23309         
23310         var cfg = {};
23311         
23312         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23313         
23314         if(this.inline){
23315             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23316         }
23317         
23318         var input =  {
23319             tag: 'input',
23320             id : id,
23321             type : this.inputType,
23322             value : this.inputValue,
23323             cls : 'roo-' + this.inputType, //'form-box',
23324             placeholder : this.placeholder || ''
23325             
23326         };
23327         
23328         if(this.inputType != 'radio'){
23329             var hidden =  {
23330                 tag: 'input',
23331                 type : 'hidden',
23332                 cls : 'roo-hidden-value',
23333                 value : this.checked ? this.inputValue : this.valueOff
23334             };
23335         }
23336         
23337             
23338         if (this.weight) { // Validity check?
23339             cfg.cls += " " + this.inputType + "-" + this.weight;
23340         }
23341         
23342         if (this.disabled) {
23343             input.disabled=true;
23344         }
23345         
23346         if(this.checked){
23347             input.checked = this.checked;
23348         }
23349         
23350         if (this.name) {
23351             
23352             input.name = this.name;
23353             
23354             if(this.inputType != 'radio'){
23355                 hidden.name = this.name;
23356                 input.name = '_hidden_' + this.name;
23357             }
23358         }
23359         
23360         if (this.size) {
23361             input.cls += ' input-' + this.size;
23362         }
23363         
23364         var settings=this;
23365         
23366         ['xs','sm','md','lg'].map(function(size){
23367             if (settings[size]) {
23368                 cfg.cls += ' col-' + size + '-' + settings[size];
23369             }
23370         });
23371         
23372         var inputblock = input;
23373          
23374         if (this.before || this.after) {
23375             
23376             inputblock = {
23377                 cls : 'input-group',
23378                 cn :  [] 
23379             };
23380             
23381             if (this.before) {
23382                 inputblock.cn.push({
23383                     tag :'span',
23384                     cls : 'input-group-addon',
23385                     html : this.before
23386                 });
23387             }
23388             
23389             inputblock.cn.push(input);
23390             
23391             if(this.inputType != 'radio'){
23392                 inputblock.cn.push(hidden);
23393             }
23394             
23395             if (this.after) {
23396                 inputblock.cn.push({
23397                     tag :'span',
23398                     cls : 'input-group-addon',
23399                     html : this.after
23400                 });
23401             }
23402             
23403         }
23404         var boxLabelCfg = false;
23405         
23406         if(this.boxLabel){
23407            
23408             boxLabelCfg = {
23409                 tag: 'label',
23410                 //'for': id, // box label is handled by onclick - so no for...
23411                 cls: 'box-label',
23412                 html: this.boxLabel
23413             };
23414             if(this.tooltip){
23415                 boxLabelCfg.tooltip = this.tooltip;
23416             }
23417              
23418         }
23419         
23420         
23421         if (align ==='left' && this.fieldLabel.length) {
23422 //                Roo.log("left and has label");
23423             cfg.cn = [
23424                 {
23425                     tag: 'label',
23426                     'for' :  id,
23427                     cls : 'control-label',
23428                     html : this.fieldLabel
23429                 },
23430                 {
23431                     cls : "", 
23432                     cn: [
23433                         inputblock
23434                     ]
23435                 }
23436             ];
23437             
23438             if (boxLabelCfg) {
23439                 cfg.cn[1].cn.push(boxLabelCfg);
23440             }
23441             
23442             if(this.labelWidth > 12){
23443                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23444             }
23445             
23446             if(this.labelWidth < 13 && this.labelmd == 0){
23447                 this.labelmd = this.labelWidth;
23448             }
23449             
23450             if(this.labellg > 0){
23451                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23452                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23453             }
23454             
23455             if(this.labelmd > 0){
23456                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23457                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23458             }
23459             
23460             if(this.labelsm > 0){
23461                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23462                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23463             }
23464             
23465             if(this.labelxs > 0){
23466                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23467                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23468             }
23469             
23470         } else if ( this.fieldLabel.length) {
23471 //                Roo.log(" label");
23472                 cfg.cn = [
23473                    
23474                     {
23475                         tag: this.boxLabel ? 'span' : 'label',
23476                         'for': id,
23477                         cls: 'control-label box-input-label',
23478                         //cls : 'input-group-addon',
23479                         html : this.fieldLabel
23480                     },
23481                     
23482                     inputblock
23483                     
23484                 ];
23485                 if (boxLabelCfg) {
23486                     cfg.cn.push(boxLabelCfg);
23487                 }
23488
23489         } else {
23490             
23491 //                Roo.log(" no label && no align");
23492                 cfg.cn = [  inputblock ] ;
23493                 if (boxLabelCfg) {
23494                     cfg.cn.push(boxLabelCfg);
23495                 }
23496
23497                 
23498         }
23499         
23500        
23501         
23502         if(this.inputType != 'radio'){
23503             cfg.cn.push(hidden);
23504         }
23505         
23506         return cfg;
23507         
23508     },
23509     
23510     /**
23511      * return the real input element.
23512      */
23513     inputEl: function ()
23514     {
23515         return this.el.select('input.roo-' + this.inputType,true).first();
23516     },
23517     hiddenEl: function ()
23518     {
23519         return this.el.select('input.roo-hidden-value',true).first();
23520     },
23521     
23522     labelEl: function()
23523     {
23524         return this.el.select('label.control-label',true).first();
23525     },
23526     /* depricated... */
23527     
23528     label: function()
23529     {
23530         return this.labelEl();
23531     },
23532     
23533     boxLabelEl: function()
23534     {
23535         return this.el.select('label.box-label',true).first();
23536     },
23537     
23538     initEvents : function()
23539     {
23540 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23541         
23542         this.inputEl().on('click', this.onClick,  this);
23543         
23544         if (this.boxLabel) { 
23545             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23546         }
23547         
23548         this.startValue = this.getValue();
23549         
23550         if(this.groupId){
23551             Roo.bootstrap.CheckBox.register(this);
23552         }
23553     },
23554     
23555     onClick : function(e)
23556     {   
23557         if(this.fireEvent('click', this, e) !== false){
23558             this.setChecked(!this.checked);
23559         }
23560         
23561     },
23562     
23563     setChecked : function(state,suppressEvent)
23564     {
23565         this.startValue = this.getValue();
23566
23567         if(this.inputType == 'radio'){
23568             
23569             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23570                 e.dom.checked = false;
23571             });
23572             
23573             this.inputEl().dom.checked = true;
23574             
23575             this.inputEl().dom.value = this.inputValue;
23576             
23577             if(suppressEvent !== true){
23578                 this.fireEvent('check', this, true);
23579             }
23580             
23581             this.validate();
23582             
23583             return;
23584         }
23585         
23586         this.checked = state;
23587         
23588         this.inputEl().dom.checked = state;
23589         
23590         
23591         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23592         
23593         if(suppressEvent !== true){
23594             this.fireEvent('check', this, state);
23595         }
23596         
23597         this.validate();
23598     },
23599     
23600     getValue : function()
23601     {
23602         if(this.inputType == 'radio'){
23603             return this.getGroupValue();
23604         }
23605         
23606         return this.hiddenEl().dom.value;
23607         
23608     },
23609     
23610     getGroupValue : function()
23611     {
23612         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23613             return '';
23614         }
23615         
23616         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23617     },
23618     
23619     setValue : function(v,suppressEvent)
23620     {
23621         if(this.inputType == 'radio'){
23622             this.setGroupValue(v, suppressEvent);
23623             return;
23624         }
23625         
23626         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23627         
23628         this.validate();
23629     },
23630     
23631     setGroupValue : function(v, suppressEvent)
23632     {
23633         this.startValue = this.getValue();
23634         
23635         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23636             e.dom.checked = false;
23637             
23638             if(e.dom.value == v){
23639                 e.dom.checked = true;
23640             }
23641         });
23642         
23643         if(suppressEvent !== true){
23644             this.fireEvent('check', this, true);
23645         }
23646
23647         this.validate();
23648         
23649         return;
23650     },
23651     
23652     validate : function()
23653     {
23654         if(this.getVisibilityEl().hasClass('hidden')){
23655             return true;
23656         }
23657         
23658         if(
23659                 this.disabled || 
23660                 (this.inputType == 'radio' && this.validateRadio()) ||
23661                 (this.inputType == 'checkbox' && this.validateCheckbox())
23662         ){
23663             this.markValid();
23664             return true;
23665         }
23666         
23667         this.markInvalid();
23668         return false;
23669     },
23670     
23671     validateRadio : function()
23672     {
23673         if(this.getVisibilityEl().hasClass('hidden')){
23674             return true;
23675         }
23676         
23677         if(this.allowBlank){
23678             return true;
23679         }
23680         
23681         var valid = false;
23682         
23683         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23684             if(!e.dom.checked){
23685                 return;
23686             }
23687             
23688             valid = true;
23689             
23690             return false;
23691         });
23692         
23693         return valid;
23694     },
23695     
23696     validateCheckbox : function()
23697     {
23698         if(!this.groupId){
23699             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23700             //return (this.getValue() == this.inputValue) ? true : false;
23701         }
23702         
23703         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23704         
23705         if(!group){
23706             return false;
23707         }
23708         
23709         var r = false;
23710         
23711         for(var i in group){
23712             if(group[i].el.isVisible(true)){
23713                 r = false;
23714                 break;
23715             }
23716             
23717             r = true;
23718         }
23719         
23720         for(var i in group){
23721             if(r){
23722                 break;
23723             }
23724             
23725             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23726         }
23727         
23728         return r;
23729     },
23730     
23731     /**
23732      * Mark this field as valid
23733      */
23734     markValid : function()
23735     {
23736         var _this = this;
23737         
23738         this.fireEvent('valid', this);
23739         
23740         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23741         
23742         if(this.groupId){
23743             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23744         }
23745         
23746         if(label){
23747             label.markValid();
23748         }
23749
23750         if(this.inputType == 'radio'){
23751             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23752                 var fg = e.findParent('.form-group', false, true);
23753                 if (Roo.bootstrap.version == 3) {
23754                     fg.removeClass([_this.invalidClass, _this.validClass]);
23755                     fg.addClass(_this.validClass);
23756                 } else {
23757                     fg.removeClass(['is-valid', 'is-invalid']);
23758                     fg.addClass('is-valid');
23759                 }
23760             });
23761             
23762             return;
23763         }
23764
23765         if(!this.groupId){
23766             var fg = this.el.findParent('.form-group', false, true);
23767             if (Roo.bootstrap.version == 3) {
23768                 fg.removeClass([this.invalidClass, this.validClass]);
23769                 fg.addClass(this.validClass);
23770             } else {
23771                 fg.removeClass(['is-valid', 'is-invalid']);
23772                 fg.addClass('is-valid');
23773             }
23774             return;
23775         }
23776         
23777         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23778         
23779         if(!group){
23780             return;
23781         }
23782         
23783         for(var i in group){
23784             var fg = group[i].el.findParent('.form-group', false, true);
23785             if (Roo.bootstrap.version == 3) {
23786                 fg.removeClass([this.invalidClass, this.validClass]);
23787                 fg.addClass(this.validClass);
23788             } else {
23789                 fg.removeClass(['is-valid', 'is-invalid']);
23790                 fg.addClass('is-valid');
23791             }
23792         }
23793     },
23794     
23795      /**
23796      * Mark this field as invalid
23797      * @param {String} msg The validation message
23798      */
23799     markInvalid : function(msg)
23800     {
23801         if(this.allowBlank){
23802             return;
23803         }
23804         
23805         var _this = this;
23806         
23807         this.fireEvent('invalid', this, msg);
23808         
23809         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23810         
23811         if(this.groupId){
23812             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23813         }
23814         
23815         if(label){
23816             label.markInvalid();
23817         }
23818             
23819         if(this.inputType == 'radio'){
23820             
23821             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23822                 var fg = e.findParent('.form-group', false, true);
23823                 if (Roo.bootstrap.version == 3) {
23824                     fg.removeClass([_this.invalidClass, _this.validClass]);
23825                     fg.addClass(_this.invalidClass);
23826                 } else {
23827                     fg.removeClass(['is-invalid', 'is-valid']);
23828                     fg.addClass('is-invalid');
23829                 }
23830             });
23831             
23832             return;
23833         }
23834         
23835         if(!this.groupId){
23836             var fg = this.el.findParent('.form-group', false, true);
23837             if (Roo.bootstrap.version == 3) {
23838                 fg.removeClass([_this.invalidClass, _this.validClass]);
23839                 fg.addClass(_this.invalidClass);
23840             } else {
23841                 fg.removeClass(['is-invalid', 'is-valid']);
23842                 fg.addClass('is-invalid');
23843             }
23844             return;
23845         }
23846         
23847         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23848         
23849         if(!group){
23850             return;
23851         }
23852         
23853         for(var i in group){
23854             var fg = group[i].el.findParent('.form-group', false, true);
23855             if (Roo.bootstrap.version == 3) {
23856                 fg.removeClass([_this.invalidClass, _this.validClass]);
23857                 fg.addClass(_this.invalidClass);
23858             } else {
23859                 fg.removeClass(['is-invalid', 'is-valid']);
23860                 fg.addClass('is-invalid');
23861             }
23862         }
23863         
23864     },
23865     
23866     clearInvalid : function()
23867     {
23868         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23869         
23870         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23871         
23872         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23873         
23874         if (label && label.iconEl) {
23875             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23876             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23877         }
23878     },
23879     
23880     disable : function()
23881     {
23882         if(this.inputType != 'radio'){
23883             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23884             return;
23885         }
23886         
23887         var _this = this;
23888         
23889         if(this.rendered){
23890             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23891                 _this.getActionEl().addClass(this.disabledClass);
23892                 e.dom.disabled = true;
23893             });
23894         }
23895         
23896         this.disabled = true;
23897         this.fireEvent("disable", this);
23898         return this;
23899     },
23900
23901     enable : function()
23902     {
23903         if(this.inputType != 'radio'){
23904             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23905             return;
23906         }
23907         
23908         var _this = this;
23909         
23910         if(this.rendered){
23911             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23912                 _this.getActionEl().removeClass(this.disabledClass);
23913                 e.dom.disabled = false;
23914             });
23915         }
23916         
23917         this.disabled = false;
23918         this.fireEvent("enable", this);
23919         return this;
23920     },
23921     
23922     setBoxLabel : function(v)
23923     {
23924         this.boxLabel = v;
23925         
23926         if(this.rendered){
23927             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23928         }
23929     }
23930
23931 });
23932
23933 Roo.apply(Roo.bootstrap.CheckBox, {
23934     
23935     groups: {},
23936     
23937      /**
23938     * register a CheckBox Group
23939     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23940     */
23941     register : function(checkbox)
23942     {
23943         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23944             this.groups[checkbox.groupId] = {};
23945         }
23946         
23947         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23948             return;
23949         }
23950         
23951         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23952         
23953     },
23954     /**
23955     * fetch a CheckBox Group based on the group ID
23956     * @param {string} the group ID
23957     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23958     */
23959     get: function(groupId) {
23960         if (typeof(this.groups[groupId]) == 'undefined') {
23961             return false;
23962         }
23963         
23964         return this.groups[groupId] ;
23965     }
23966     
23967     
23968 });
23969 /*
23970  * - LGPL
23971  *
23972  * RadioItem
23973  * 
23974  */
23975
23976 /**
23977  * @class Roo.bootstrap.Radio
23978  * @extends Roo.bootstrap.Component
23979  * Bootstrap Radio class
23980  * @cfg {String} boxLabel - the label associated
23981  * @cfg {String} value - the value of radio
23982  * 
23983  * @constructor
23984  * Create a new Radio
23985  * @param {Object} config The config object
23986  */
23987 Roo.bootstrap.Radio = function(config){
23988     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23989     
23990 };
23991
23992 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23993     
23994     boxLabel : '',
23995     
23996     value : '',
23997     
23998     getAutoCreate : function()
23999     {
24000         var cfg = {
24001             tag : 'div',
24002             cls : 'form-group radio',
24003             cn : [
24004                 {
24005                     tag : 'label',
24006                     cls : 'box-label',
24007                     html : this.boxLabel
24008                 }
24009             ]
24010         };
24011         
24012         return cfg;
24013     },
24014     
24015     initEvents : function() 
24016     {
24017         this.parent().register(this);
24018         
24019         this.el.on('click', this.onClick, this);
24020         
24021     },
24022     
24023     onClick : function(e)
24024     {
24025         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24026             this.setChecked(true);
24027         }
24028     },
24029     
24030     setChecked : function(state, suppressEvent)
24031     {
24032         this.parent().setValue(this.value, suppressEvent);
24033         
24034     },
24035     
24036     setBoxLabel : function(v)
24037     {
24038         this.boxLabel = v;
24039         
24040         if(this.rendered){
24041             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24042         }
24043     }
24044     
24045 });
24046  
24047
24048  /*
24049  * - LGPL
24050  *
24051  * Input
24052  * 
24053  */
24054
24055 /**
24056  * @class Roo.bootstrap.SecurePass
24057  * @extends Roo.bootstrap.Input
24058  * Bootstrap SecurePass class
24059  *
24060  * 
24061  * @constructor
24062  * Create a new SecurePass
24063  * @param {Object} config The config object
24064  */
24065  
24066 Roo.bootstrap.SecurePass = function (config) {
24067     // these go here, so the translation tool can replace them..
24068     this.errors = {
24069         PwdEmpty: "Please type a password, and then retype it to confirm.",
24070         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24071         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24072         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24073         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24074         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24075         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24076         TooWeak: "Your password is Too Weak."
24077     },
24078     this.meterLabel = "Password strength:";
24079     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24080     this.meterClass = [
24081         "roo-password-meter-tooweak", 
24082         "roo-password-meter-weak", 
24083         "roo-password-meter-medium", 
24084         "roo-password-meter-strong", 
24085         "roo-password-meter-grey"
24086     ];
24087     
24088     this.errors = {};
24089     
24090     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24091 }
24092
24093 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24094     /**
24095      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24096      * {
24097      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24098      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24099      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24100      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24101      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24102      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24103      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24104      * })
24105      */
24106     // private
24107     
24108     meterWidth: 300,
24109     errorMsg :'',    
24110     errors: false,
24111     imageRoot: '/',
24112     /**
24113      * @cfg {String/Object} Label for the strength meter (defaults to
24114      * 'Password strength:')
24115      */
24116     // private
24117     meterLabel: '',
24118     /**
24119      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24120      * ['Weak', 'Medium', 'Strong'])
24121      */
24122     // private    
24123     pwdStrengths: false,    
24124     // private
24125     strength: 0,
24126     // private
24127     _lastPwd: null,
24128     // private
24129     kCapitalLetter: 0,
24130     kSmallLetter: 1,
24131     kDigit: 2,
24132     kPunctuation: 3,
24133     
24134     insecure: false,
24135     // private
24136     initEvents: function ()
24137     {
24138         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24139
24140         if (this.el.is('input[type=password]') && Roo.isSafari) {
24141             this.el.on('keydown', this.SafariOnKeyDown, this);
24142         }
24143
24144         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24145     },
24146     // private
24147     onRender: function (ct, position)
24148     {
24149         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24150         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24151         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24152
24153         this.trigger.createChild({
24154                    cn: [
24155                     {
24156                     //id: 'PwdMeter',
24157                     tag: 'div',
24158                     cls: 'roo-password-meter-grey col-xs-12',
24159                     style: {
24160                         //width: 0,
24161                         //width: this.meterWidth + 'px'                                                
24162                         }
24163                     },
24164                     {                            
24165                          cls: 'roo-password-meter-text'                          
24166                     }
24167                 ]            
24168         });
24169
24170          
24171         if (this.hideTrigger) {
24172             this.trigger.setDisplayed(false);
24173         }
24174         this.setSize(this.width || '', this.height || '');
24175     },
24176     // private
24177     onDestroy: function ()
24178     {
24179         if (this.trigger) {
24180             this.trigger.removeAllListeners();
24181             this.trigger.remove();
24182         }
24183         if (this.wrap) {
24184             this.wrap.remove();
24185         }
24186         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24187     },
24188     // private
24189     checkStrength: function ()
24190     {
24191         var pwd = this.inputEl().getValue();
24192         if (pwd == this._lastPwd) {
24193             return;
24194         }
24195
24196         var strength;
24197         if (this.ClientSideStrongPassword(pwd)) {
24198             strength = 3;
24199         } else if (this.ClientSideMediumPassword(pwd)) {
24200             strength = 2;
24201         } else if (this.ClientSideWeakPassword(pwd)) {
24202             strength = 1;
24203         } else {
24204             strength = 0;
24205         }
24206         
24207         Roo.log('strength1: ' + strength);
24208         
24209         //var pm = this.trigger.child('div/div/div').dom;
24210         var pm = this.trigger.child('div/div');
24211         pm.removeClass(this.meterClass);
24212         pm.addClass(this.meterClass[strength]);
24213                 
24214         
24215         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24216                 
24217         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24218         
24219         this._lastPwd = pwd;
24220     },
24221     reset: function ()
24222     {
24223         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24224         
24225         this._lastPwd = '';
24226         
24227         var pm = this.trigger.child('div/div');
24228         pm.removeClass(this.meterClass);
24229         pm.addClass('roo-password-meter-grey');        
24230         
24231         
24232         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24233         
24234         pt.innerHTML = '';
24235         this.inputEl().dom.type='password';
24236     },
24237     // private
24238     validateValue: function (value)
24239     {
24240         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24241             return false;
24242         }
24243         if (value.length == 0) {
24244             if (this.allowBlank) {
24245                 this.clearInvalid();
24246                 return true;
24247             }
24248
24249             this.markInvalid(this.errors.PwdEmpty);
24250             this.errorMsg = this.errors.PwdEmpty;
24251             return false;
24252         }
24253         
24254         if(this.insecure){
24255             return true;
24256         }
24257         
24258         if (!value.match(/[\x21-\x7e]+/)) {
24259             this.markInvalid(this.errors.PwdBadChar);
24260             this.errorMsg = this.errors.PwdBadChar;
24261             return false;
24262         }
24263         if (value.length < 6) {
24264             this.markInvalid(this.errors.PwdShort);
24265             this.errorMsg = this.errors.PwdShort;
24266             return false;
24267         }
24268         if (value.length > 16) {
24269             this.markInvalid(this.errors.PwdLong);
24270             this.errorMsg = this.errors.PwdLong;
24271             return false;
24272         }
24273         var strength;
24274         if (this.ClientSideStrongPassword(value)) {
24275             strength = 3;
24276         } else if (this.ClientSideMediumPassword(value)) {
24277             strength = 2;
24278         } else if (this.ClientSideWeakPassword(value)) {
24279             strength = 1;
24280         } else {
24281             strength = 0;
24282         }
24283
24284         
24285         if (strength < 2) {
24286             //this.markInvalid(this.errors.TooWeak);
24287             this.errorMsg = this.errors.TooWeak;
24288             //return false;
24289         }
24290         
24291         
24292         console.log('strength2: ' + strength);
24293         
24294         //var pm = this.trigger.child('div/div/div').dom;
24295         
24296         var pm = this.trigger.child('div/div');
24297         pm.removeClass(this.meterClass);
24298         pm.addClass(this.meterClass[strength]);
24299                 
24300         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24301                 
24302         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24303         
24304         this.errorMsg = ''; 
24305         return true;
24306     },
24307     // private
24308     CharacterSetChecks: function (type)
24309     {
24310         this.type = type;
24311         this.fResult = false;
24312     },
24313     // private
24314     isctype: function (character, type)
24315     {
24316         switch (type) {  
24317             case this.kCapitalLetter:
24318                 if (character >= 'A' && character <= 'Z') {
24319                     return true;
24320                 }
24321                 break;
24322             
24323             case this.kSmallLetter:
24324                 if (character >= 'a' && character <= 'z') {
24325                     return true;
24326                 }
24327                 break;
24328             
24329             case this.kDigit:
24330                 if (character >= '0' && character <= '9') {
24331                     return true;
24332                 }
24333                 break;
24334             
24335             case this.kPunctuation:
24336                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24337                     return true;
24338                 }
24339                 break;
24340             
24341             default:
24342                 return false;
24343         }
24344
24345     },
24346     // private
24347     IsLongEnough: function (pwd, size)
24348     {
24349         return !(pwd == null || isNaN(size) || pwd.length < size);
24350     },
24351     // private
24352     SpansEnoughCharacterSets: function (word, nb)
24353     {
24354         if (!this.IsLongEnough(word, nb))
24355         {
24356             return false;
24357         }
24358
24359         var characterSetChecks = new Array(
24360             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24361             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24362         );
24363         
24364         for (var index = 0; index < word.length; ++index) {
24365             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24366                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24367                     characterSetChecks[nCharSet].fResult = true;
24368                     break;
24369                 }
24370             }
24371         }
24372
24373         var nCharSets = 0;
24374         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24375             if (characterSetChecks[nCharSet].fResult) {
24376                 ++nCharSets;
24377             }
24378         }
24379
24380         if (nCharSets < nb) {
24381             return false;
24382         }
24383         return true;
24384     },
24385     // private
24386     ClientSideStrongPassword: function (pwd)
24387     {
24388         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24389     },
24390     // private
24391     ClientSideMediumPassword: function (pwd)
24392     {
24393         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24394     },
24395     // private
24396     ClientSideWeakPassword: function (pwd)
24397     {
24398         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24399     }
24400           
24401 })//<script type="text/javascript">
24402
24403 /*
24404  * Based  Ext JS Library 1.1.1
24405  * Copyright(c) 2006-2007, Ext JS, LLC.
24406  * LGPL
24407  *
24408  */
24409  
24410 /**
24411  * @class Roo.HtmlEditorCore
24412  * @extends Roo.Component
24413  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24414  *
24415  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24416  */
24417
24418 Roo.HtmlEditorCore = function(config){
24419     
24420     
24421     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24422     
24423     
24424     this.addEvents({
24425         /**
24426          * @event initialize
24427          * Fires when the editor is fully initialized (including the iframe)
24428          * @param {Roo.HtmlEditorCore} this
24429          */
24430         initialize: true,
24431         /**
24432          * @event activate
24433          * Fires when the editor is first receives the focus. Any insertion must wait
24434          * until after this event.
24435          * @param {Roo.HtmlEditorCore} this
24436          */
24437         activate: true,
24438          /**
24439          * @event beforesync
24440          * Fires before the textarea is updated with content from the editor iframe. Return false
24441          * to cancel the sync.
24442          * @param {Roo.HtmlEditorCore} this
24443          * @param {String} html
24444          */
24445         beforesync: true,
24446          /**
24447          * @event beforepush
24448          * Fires before the iframe editor is updated with content from the textarea. Return false
24449          * to cancel the push.
24450          * @param {Roo.HtmlEditorCore} this
24451          * @param {String} html
24452          */
24453         beforepush: true,
24454          /**
24455          * @event sync
24456          * Fires when the textarea is updated with content from the editor iframe.
24457          * @param {Roo.HtmlEditorCore} this
24458          * @param {String} html
24459          */
24460         sync: true,
24461          /**
24462          * @event push
24463          * Fires when the iframe editor is updated with content from the textarea.
24464          * @param {Roo.HtmlEditorCore} this
24465          * @param {String} html
24466          */
24467         push: true,
24468         
24469         /**
24470          * @event editorevent
24471          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24472          * @param {Roo.HtmlEditorCore} this
24473          */
24474         editorevent: true
24475         
24476     });
24477     
24478     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24479     
24480     // defaults : white / black...
24481     this.applyBlacklists();
24482     
24483     
24484     
24485 };
24486
24487
24488 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24489
24490
24491      /**
24492      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24493      */
24494     
24495     owner : false,
24496     
24497      /**
24498      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24499      *                        Roo.resizable.
24500      */
24501     resizable : false,
24502      /**
24503      * @cfg {Number} height (in pixels)
24504      */   
24505     height: 300,
24506    /**
24507      * @cfg {Number} width (in pixels)
24508      */   
24509     width: 500,
24510     
24511     /**
24512      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24513      * 
24514      */
24515     stylesheets: false,
24516     
24517     // id of frame..
24518     frameId: false,
24519     
24520     // private properties
24521     validationEvent : false,
24522     deferHeight: true,
24523     initialized : false,
24524     activated : false,
24525     sourceEditMode : false,
24526     onFocus : Roo.emptyFn,
24527     iframePad:3,
24528     hideMode:'offsets',
24529     
24530     clearUp: true,
24531     
24532     // blacklist + whitelisted elements..
24533     black: false,
24534     white: false,
24535      
24536     bodyCls : '',
24537
24538     /**
24539      * Protected method that will not generally be called directly. It
24540      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24541      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24542      */
24543     getDocMarkup : function(){
24544         // body styles..
24545         var st = '';
24546         
24547         // inherit styels from page...?? 
24548         if (this.stylesheets === false) {
24549             
24550             Roo.get(document.head).select('style').each(function(node) {
24551                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24552             });
24553             
24554             Roo.get(document.head).select('link').each(function(node) { 
24555                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24556             });
24557             
24558         } else if (!this.stylesheets.length) {
24559                 // simple..
24560                 st = '<style type="text/css">' +
24561                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24562                    '</style>';
24563         } else {
24564             for (var i in this.stylesheets) { 
24565                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24566             }
24567             
24568         }
24569         
24570         st +=  '<style type="text/css">' +
24571             'IMG { cursor: pointer } ' +
24572         '</style>';
24573
24574         var cls = 'roo-htmleditor-body';
24575         
24576         if(this.bodyCls.length){
24577             cls += ' ' + this.bodyCls;
24578         }
24579         
24580         return '<html><head>' + st  +
24581             //<style type="text/css">' +
24582             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24583             //'</style>' +
24584             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24585     },
24586
24587     // private
24588     onRender : function(ct, position)
24589     {
24590         var _t = this;
24591         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24592         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24593         
24594         
24595         this.el.dom.style.border = '0 none';
24596         this.el.dom.setAttribute('tabIndex', -1);
24597         this.el.addClass('x-hidden hide');
24598         
24599         
24600         
24601         if(Roo.isIE){ // fix IE 1px bogus margin
24602             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24603         }
24604        
24605         
24606         this.frameId = Roo.id();
24607         
24608          
24609         
24610         var iframe = this.owner.wrap.createChild({
24611             tag: 'iframe',
24612             cls: 'form-control', // bootstrap..
24613             id: this.frameId,
24614             name: this.frameId,
24615             frameBorder : 'no',
24616             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24617         }, this.el
24618         );
24619         
24620         
24621         this.iframe = iframe.dom;
24622
24623          this.assignDocWin();
24624         
24625         this.doc.designMode = 'on';
24626        
24627         this.doc.open();
24628         this.doc.write(this.getDocMarkup());
24629         this.doc.close();
24630
24631         
24632         var task = { // must defer to wait for browser to be ready
24633             run : function(){
24634                 //console.log("run task?" + this.doc.readyState);
24635                 this.assignDocWin();
24636                 if(this.doc.body || this.doc.readyState == 'complete'){
24637                     try {
24638                         this.doc.designMode="on";
24639                     } catch (e) {
24640                         return;
24641                     }
24642                     Roo.TaskMgr.stop(task);
24643                     this.initEditor.defer(10, this);
24644                 }
24645             },
24646             interval : 10,
24647             duration: 10000,
24648             scope: this
24649         };
24650         Roo.TaskMgr.start(task);
24651
24652     },
24653
24654     // private
24655     onResize : function(w, h)
24656     {
24657          Roo.log('resize: ' +w + ',' + h );
24658         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24659         if(!this.iframe){
24660             return;
24661         }
24662         if(typeof w == 'number'){
24663             
24664             this.iframe.style.width = w + 'px';
24665         }
24666         if(typeof h == 'number'){
24667             
24668             this.iframe.style.height = h + 'px';
24669             if(this.doc){
24670                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24671             }
24672         }
24673         
24674     },
24675
24676     /**
24677      * Toggles the editor between standard and source edit mode.
24678      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24679      */
24680     toggleSourceEdit : function(sourceEditMode){
24681         
24682         this.sourceEditMode = sourceEditMode === true;
24683         
24684         if(this.sourceEditMode){
24685  
24686             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24687             
24688         }else{
24689             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24690             //this.iframe.className = '';
24691             this.deferFocus();
24692         }
24693         //this.setSize(this.owner.wrap.getSize());
24694         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24695     },
24696
24697     
24698   
24699
24700     /**
24701      * Protected method that will not generally be called directly. If you need/want
24702      * custom HTML cleanup, this is the method you should override.
24703      * @param {String} html The HTML to be cleaned
24704      * return {String} The cleaned HTML
24705      */
24706     cleanHtml : function(html){
24707         html = String(html);
24708         if(html.length > 5){
24709             if(Roo.isSafari){ // strip safari nonsense
24710                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24711             }
24712         }
24713         if(html == '&nbsp;'){
24714             html = '';
24715         }
24716         return html;
24717     },
24718
24719     /**
24720      * HTML Editor -> Textarea
24721      * Protected method that will not generally be called directly. Syncs the contents
24722      * of the editor iframe with the textarea.
24723      */
24724     syncValue : function(){
24725         if(this.initialized){
24726             var bd = (this.doc.body || this.doc.documentElement);
24727             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24728             var html = bd.innerHTML;
24729             if(Roo.isSafari){
24730                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24731                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24732                 if(m && m[1]){
24733                     html = '<div style="'+m[0]+'">' + html + '</div>';
24734                 }
24735             }
24736             html = this.cleanHtml(html);
24737             // fix up the special chars.. normaly like back quotes in word...
24738             // however we do not want to do this with chinese..
24739             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24740                 
24741                 var cc = match.charCodeAt();
24742
24743                 // Get the character value, handling surrogate pairs
24744                 if (match.length == 2) {
24745                     // It's a surrogate pair, calculate the Unicode code point
24746                     var high = match.charCodeAt(0) - 0xD800;
24747                     var low  = match.charCodeAt(1) - 0xDC00;
24748                     cc = (high * 0x400) + low + 0x10000;
24749                 }  else if (
24750                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24751                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24752                     (cc >= 0xf900 && cc < 0xfb00 )
24753                 ) {
24754                         return match;
24755                 }  
24756          
24757                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24758                 return "&#" + cc + ";";
24759                 
24760                 
24761             });
24762             
24763             
24764              
24765             if(this.owner.fireEvent('beforesync', this, html) !== false){
24766                 this.el.dom.value = html;
24767                 this.owner.fireEvent('sync', this, html);
24768             }
24769         }
24770     },
24771
24772     /**
24773      * Protected method that will not generally be called directly. Pushes the value of the textarea
24774      * into the iframe editor.
24775      */
24776     pushValue : function(){
24777         if(this.initialized){
24778             var v = this.el.dom.value.trim();
24779             
24780 //            if(v.length < 1){
24781 //                v = '&#160;';
24782 //            }
24783             
24784             if(this.owner.fireEvent('beforepush', this, v) !== false){
24785                 var d = (this.doc.body || this.doc.documentElement);
24786                 d.innerHTML = v;
24787                 this.cleanUpPaste();
24788                 this.el.dom.value = d.innerHTML;
24789                 this.owner.fireEvent('push', this, v);
24790             }
24791         }
24792     },
24793
24794     // private
24795     deferFocus : function(){
24796         this.focus.defer(10, this);
24797     },
24798
24799     // doc'ed in Field
24800     focus : function(){
24801         if(this.win && !this.sourceEditMode){
24802             this.win.focus();
24803         }else{
24804             this.el.focus();
24805         }
24806     },
24807     
24808     assignDocWin: function()
24809     {
24810         var iframe = this.iframe;
24811         
24812          if(Roo.isIE){
24813             this.doc = iframe.contentWindow.document;
24814             this.win = iframe.contentWindow;
24815         } else {
24816 //            if (!Roo.get(this.frameId)) {
24817 //                return;
24818 //            }
24819 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24820 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24821             
24822             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24823                 return;
24824             }
24825             
24826             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24827             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24828         }
24829     },
24830     
24831     // private
24832     initEditor : function(){
24833         //console.log("INIT EDITOR");
24834         this.assignDocWin();
24835         
24836         
24837         
24838         this.doc.designMode="on";
24839         this.doc.open();
24840         this.doc.write(this.getDocMarkup());
24841         this.doc.close();
24842         
24843         var dbody = (this.doc.body || this.doc.documentElement);
24844         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24845         // this copies styles from the containing element into thsi one..
24846         // not sure why we need all of this..
24847         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24848         
24849         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24850         //ss['background-attachment'] = 'fixed'; // w3c
24851         dbody.bgProperties = 'fixed'; // ie
24852         //Roo.DomHelper.applyStyles(dbody, ss);
24853         Roo.EventManager.on(this.doc, {
24854             //'mousedown': this.onEditorEvent,
24855             'mouseup': this.onEditorEvent,
24856             'dblclick': this.onEditorEvent,
24857             'click': this.onEditorEvent,
24858             'keyup': this.onEditorEvent,
24859             buffer:100,
24860             scope: this
24861         });
24862         if(Roo.isGecko){
24863             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24864         }
24865         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24866             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24867         }
24868         this.initialized = true;
24869
24870         this.owner.fireEvent('initialize', this);
24871         this.pushValue();
24872     },
24873
24874     // private
24875     onDestroy : function(){
24876         
24877         
24878         
24879         if(this.rendered){
24880             
24881             //for (var i =0; i < this.toolbars.length;i++) {
24882             //    // fixme - ask toolbars for heights?
24883             //    this.toolbars[i].onDestroy();
24884            // }
24885             
24886             //this.wrap.dom.innerHTML = '';
24887             //this.wrap.remove();
24888         }
24889     },
24890
24891     // private
24892     onFirstFocus : function(){
24893         
24894         this.assignDocWin();
24895         
24896         
24897         this.activated = true;
24898          
24899     
24900         if(Roo.isGecko){ // prevent silly gecko errors
24901             this.win.focus();
24902             var s = this.win.getSelection();
24903             if(!s.focusNode || s.focusNode.nodeType != 3){
24904                 var r = s.getRangeAt(0);
24905                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24906                 r.collapse(true);
24907                 this.deferFocus();
24908             }
24909             try{
24910                 this.execCmd('useCSS', true);
24911                 this.execCmd('styleWithCSS', false);
24912             }catch(e){}
24913         }
24914         this.owner.fireEvent('activate', this);
24915     },
24916
24917     // private
24918     adjustFont: function(btn){
24919         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24920         //if(Roo.isSafari){ // safari
24921         //    adjust *= 2;
24922        // }
24923         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24924         if(Roo.isSafari){ // safari
24925             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24926             v =  (v < 10) ? 10 : v;
24927             v =  (v > 48) ? 48 : v;
24928             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24929             
24930         }
24931         
24932         
24933         v = Math.max(1, v+adjust);
24934         
24935         this.execCmd('FontSize', v  );
24936     },
24937
24938     onEditorEvent : function(e)
24939     {
24940         this.owner.fireEvent('editorevent', this, e);
24941       //  this.updateToolbar();
24942         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24943     },
24944
24945     insertTag : function(tg)
24946     {
24947         // could be a bit smarter... -> wrap the current selected tRoo..
24948         if (tg.toLowerCase() == 'span' ||
24949             tg.toLowerCase() == 'code' ||
24950             tg.toLowerCase() == 'sup' ||
24951             tg.toLowerCase() == 'sub' 
24952             ) {
24953             
24954             range = this.createRange(this.getSelection());
24955             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24956             wrappingNode.appendChild(range.extractContents());
24957             range.insertNode(wrappingNode);
24958
24959             return;
24960             
24961             
24962             
24963         }
24964         this.execCmd("formatblock",   tg);
24965         
24966     },
24967     
24968     insertText : function(txt)
24969     {
24970         
24971         
24972         var range = this.createRange();
24973         range.deleteContents();
24974                //alert(Sender.getAttribute('label'));
24975                
24976         range.insertNode(this.doc.createTextNode(txt));
24977     } ,
24978     
24979      
24980
24981     /**
24982      * Executes a Midas editor command on the editor document and performs necessary focus and
24983      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24984      * @param {String} cmd The Midas command
24985      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24986      */
24987     relayCmd : function(cmd, value){
24988         this.win.focus();
24989         this.execCmd(cmd, value);
24990         this.owner.fireEvent('editorevent', this);
24991         //this.updateToolbar();
24992         this.owner.deferFocus();
24993     },
24994
24995     /**
24996      * Executes a Midas editor command directly on the editor document.
24997      * For visual commands, you should use {@link #relayCmd} instead.
24998      * <b>This should only be called after the editor is initialized.</b>
24999      * @param {String} cmd The Midas command
25000      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25001      */
25002     execCmd : function(cmd, value){
25003         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25004         this.syncValue();
25005     },
25006  
25007  
25008    
25009     /**
25010      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25011      * to insert tRoo.
25012      * @param {String} text | dom node.. 
25013      */
25014     insertAtCursor : function(text)
25015     {
25016         
25017         if(!this.activated){
25018             return;
25019         }
25020         /*
25021         if(Roo.isIE){
25022             this.win.focus();
25023             var r = this.doc.selection.createRange();
25024             if(r){
25025                 r.collapse(true);
25026                 r.pasteHTML(text);
25027                 this.syncValue();
25028                 this.deferFocus();
25029             
25030             }
25031             return;
25032         }
25033         */
25034         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25035             this.win.focus();
25036             
25037             
25038             // from jquery ui (MIT licenced)
25039             var range, node;
25040             var win = this.win;
25041             
25042             if (win.getSelection && win.getSelection().getRangeAt) {
25043                 range = win.getSelection().getRangeAt(0);
25044                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25045                 range.insertNode(node);
25046             } else if (win.document.selection && win.document.selection.createRange) {
25047                 // no firefox support
25048                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25049                 win.document.selection.createRange().pasteHTML(txt);
25050             } else {
25051                 // no firefox support
25052                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25053                 this.execCmd('InsertHTML', txt);
25054             } 
25055             
25056             this.syncValue();
25057             
25058             this.deferFocus();
25059         }
25060     },
25061  // private
25062     mozKeyPress : function(e){
25063         if(e.ctrlKey){
25064             var c = e.getCharCode(), cmd;
25065           
25066             if(c > 0){
25067                 c = String.fromCharCode(c).toLowerCase();
25068                 switch(c){
25069                     case 'b':
25070                         cmd = 'bold';
25071                         break;
25072                     case 'i':
25073                         cmd = 'italic';
25074                         break;
25075                     
25076                     case 'u':
25077                         cmd = 'underline';
25078                         break;
25079                     
25080                     case 'v':
25081                         this.cleanUpPaste.defer(100, this);
25082                         return;
25083                         
25084                 }
25085                 if(cmd){
25086                     this.win.focus();
25087                     this.execCmd(cmd);
25088                     this.deferFocus();
25089                     e.preventDefault();
25090                 }
25091                 
25092             }
25093         }
25094     },
25095
25096     // private
25097     fixKeys : function(){ // load time branching for fastest keydown performance
25098         if(Roo.isIE){
25099             return function(e){
25100                 var k = e.getKey(), r;
25101                 if(k == e.TAB){
25102                     e.stopEvent();
25103                     r = this.doc.selection.createRange();
25104                     if(r){
25105                         r.collapse(true);
25106                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25107                         this.deferFocus();
25108                     }
25109                     return;
25110                 }
25111                 
25112                 if(k == e.ENTER){
25113                     r = this.doc.selection.createRange();
25114                     if(r){
25115                         var target = r.parentElement();
25116                         if(!target || target.tagName.toLowerCase() != 'li'){
25117                             e.stopEvent();
25118                             r.pasteHTML('<br />');
25119                             r.collapse(false);
25120                             r.select();
25121                         }
25122                     }
25123                 }
25124                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25125                     this.cleanUpPaste.defer(100, this);
25126                     return;
25127                 }
25128                 
25129                 
25130             };
25131         }else if(Roo.isOpera){
25132             return function(e){
25133                 var k = e.getKey();
25134                 if(k == e.TAB){
25135                     e.stopEvent();
25136                     this.win.focus();
25137                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25138                     this.deferFocus();
25139                 }
25140                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25141                     this.cleanUpPaste.defer(100, this);
25142                     return;
25143                 }
25144                 
25145             };
25146         }else if(Roo.isSafari){
25147             return function(e){
25148                 var k = e.getKey();
25149                 
25150                 if(k == e.TAB){
25151                     e.stopEvent();
25152                     this.execCmd('InsertText','\t');
25153                     this.deferFocus();
25154                     return;
25155                 }
25156                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25157                     this.cleanUpPaste.defer(100, this);
25158                     return;
25159                 }
25160                 
25161              };
25162         }
25163     }(),
25164     
25165     getAllAncestors: function()
25166     {
25167         var p = this.getSelectedNode();
25168         var a = [];
25169         if (!p) {
25170             a.push(p); // push blank onto stack..
25171             p = this.getParentElement();
25172         }
25173         
25174         
25175         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25176             a.push(p);
25177             p = p.parentNode;
25178         }
25179         a.push(this.doc.body);
25180         return a;
25181     },
25182     lastSel : false,
25183     lastSelNode : false,
25184     
25185     
25186     getSelection : function() 
25187     {
25188         this.assignDocWin();
25189         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25190     },
25191     
25192     getSelectedNode: function() 
25193     {
25194         // this may only work on Gecko!!!
25195         
25196         // should we cache this!!!!
25197         
25198         
25199         
25200          
25201         var range = this.createRange(this.getSelection()).cloneRange();
25202         
25203         if (Roo.isIE) {
25204             var parent = range.parentElement();
25205             while (true) {
25206                 var testRange = range.duplicate();
25207                 testRange.moveToElementText(parent);
25208                 if (testRange.inRange(range)) {
25209                     break;
25210                 }
25211                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25212                     break;
25213                 }
25214                 parent = parent.parentElement;
25215             }
25216             return parent;
25217         }
25218         
25219         // is ancestor a text element.
25220         var ac =  range.commonAncestorContainer;
25221         if (ac.nodeType == 3) {
25222             ac = ac.parentNode;
25223         }
25224         
25225         var ar = ac.childNodes;
25226          
25227         var nodes = [];
25228         var other_nodes = [];
25229         var has_other_nodes = false;
25230         for (var i=0;i<ar.length;i++) {
25231             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25232                 continue;
25233             }
25234             // fullly contained node.
25235             
25236             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25237                 nodes.push(ar[i]);
25238                 continue;
25239             }
25240             
25241             // probably selected..
25242             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25243                 other_nodes.push(ar[i]);
25244                 continue;
25245             }
25246             // outer..
25247             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25248                 continue;
25249             }
25250             
25251             
25252             has_other_nodes = true;
25253         }
25254         if (!nodes.length && other_nodes.length) {
25255             nodes= other_nodes;
25256         }
25257         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25258             return false;
25259         }
25260         
25261         return nodes[0];
25262     },
25263     createRange: function(sel)
25264     {
25265         // this has strange effects when using with 
25266         // top toolbar - not sure if it's a great idea.
25267         //this.editor.contentWindow.focus();
25268         if (typeof sel != "undefined") {
25269             try {
25270                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25271             } catch(e) {
25272                 return this.doc.createRange();
25273             }
25274         } else {
25275             return this.doc.createRange();
25276         }
25277     },
25278     getParentElement: function()
25279     {
25280         
25281         this.assignDocWin();
25282         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25283         
25284         var range = this.createRange(sel);
25285          
25286         try {
25287             var p = range.commonAncestorContainer;
25288             while (p.nodeType == 3) { // text node
25289                 p = p.parentNode;
25290             }
25291             return p;
25292         } catch (e) {
25293             return null;
25294         }
25295     
25296     },
25297     /***
25298      *
25299      * Range intersection.. the hard stuff...
25300      *  '-1' = before
25301      *  '0' = hits..
25302      *  '1' = after.
25303      *         [ -- selected range --- ]
25304      *   [fail]                        [fail]
25305      *
25306      *    basically..
25307      *      if end is before start or  hits it. fail.
25308      *      if start is after end or hits it fail.
25309      *
25310      *   if either hits (but other is outside. - then it's not 
25311      *   
25312      *    
25313      **/
25314     
25315     
25316     // @see http://www.thismuchiknow.co.uk/?p=64.
25317     rangeIntersectsNode : function(range, node)
25318     {
25319         var nodeRange = node.ownerDocument.createRange();
25320         try {
25321             nodeRange.selectNode(node);
25322         } catch (e) {
25323             nodeRange.selectNodeContents(node);
25324         }
25325     
25326         var rangeStartRange = range.cloneRange();
25327         rangeStartRange.collapse(true);
25328     
25329         var rangeEndRange = range.cloneRange();
25330         rangeEndRange.collapse(false);
25331     
25332         var nodeStartRange = nodeRange.cloneRange();
25333         nodeStartRange.collapse(true);
25334     
25335         var nodeEndRange = nodeRange.cloneRange();
25336         nodeEndRange.collapse(false);
25337     
25338         return rangeStartRange.compareBoundaryPoints(
25339                  Range.START_TO_START, nodeEndRange) == -1 &&
25340                rangeEndRange.compareBoundaryPoints(
25341                  Range.START_TO_START, nodeStartRange) == 1;
25342         
25343          
25344     },
25345     rangeCompareNode : function(range, node)
25346     {
25347         var nodeRange = node.ownerDocument.createRange();
25348         try {
25349             nodeRange.selectNode(node);
25350         } catch (e) {
25351             nodeRange.selectNodeContents(node);
25352         }
25353         
25354         
25355         range.collapse(true);
25356     
25357         nodeRange.collapse(true);
25358      
25359         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25360         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25361          
25362         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25363         
25364         var nodeIsBefore   =  ss == 1;
25365         var nodeIsAfter    = ee == -1;
25366         
25367         if (nodeIsBefore && nodeIsAfter) {
25368             return 0; // outer
25369         }
25370         if (!nodeIsBefore && nodeIsAfter) {
25371             return 1; //right trailed.
25372         }
25373         
25374         if (nodeIsBefore && !nodeIsAfter) {
25375             return 2;  // left trailed.
25376         }
25377         // fully contined.
25378         return 3;
25379     },
25380
25381     // private? - in a new class?
25382     cleanUpPaste :  function()
25383     {
25384         // cleans up the whole document..
25385         Roo.log('cleanuppaste');
25386         
25387         this.cleanUpChildren(this.doc.body);
25388         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25389         if (clean != this.doc.body.innerHTML) {
25390             this.doc.body.innerHTML = clean;
25391         }
25392         
25393     },
25394     
25395     cleanWordChars : function(input) {// change the chars to hex code
25396         var he = Roo.HtmlEditorCore;
25397         
25398         var output = input;
25399         Roo.each(he.swapCodes, function(sw) { 
25400             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25401             
25402             output = output.replace(swapper, sw[1]);
25403         });
25404         
25405         return output;
25406     },
25407     
25408     
25409     cleanUpChildren : function (n)
25410     {
25411         if (!n.childNodes.length) {
25412             return;
25413         }
25414         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25415            this.cleanUpChild(n.childNodes[i]);
25416         }
25417     },
25418     
25419     
25420         
25421     
25422     cleanUpChild : function (node)
25423     {
25424         var ed = this;
25425         //console.log(node);
25426         if (node.nodeName == "#text") {
25427             // clean up silly Windows -- stuff?
25428             return; 
25429         }
25430         if (node.nodeName == "#comment") {
25431             node.parentNode.removeChild(node);
25432             // clean up silly Windows -- stuff?
25433             return; 
25434         }
25435         var lcname = node.tagName.toLowerCase();
25436         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25437         // whitelist of tags..
25438         
25439         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25440             // remove node.
25441             node.parentNode.removeChild(node);
25442             return;
25443             
25444         }
25445         
25446         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25447         
25448         // spans with no attributes - just remove them..
25449         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25450             remove_keep_children = true;
25451         }
25452         
25453         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25454         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25455         
25456         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25457         //    remove_keep_children = true;
25458         //}
25459         
25460         if (remove_keep_children) {
25461             this.cleanUpChildren(node);
25462             // inserts everything just before this node...
25463             while (node.childNodes.length) {
25464                 var cn = node.childNodes[0];
25465                 node.removeChild(cn);
25466                 node.parentNode.insertBefore(cn, node);
25467             }
25468             node.parentNode.removeChild(node);
25469             return;
25470         }
25471         
25472         if (!node.attributes || !node.attributes.length) {
25473             
25474           
25475             
25476             
25477             this.cleanUpChildren(node);
25478             return;
25479         }
25480         
25481         function cleanAttr(n,v)
25482         {
25483             
25484             if (v.match(/^\./) || v.match(/^\//)) {
25485                 return;
25486             }
25487             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25488                 return;
25489             }
25490             if (v.match(/^#/)) {
25491                 return;
25492             }
25493             if (v.match(/^\{/)) { // allow template editing.
25494                 return;
25495             }
25496 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25497             node.removeAttribute(n);
25498             
25499         }
25500         
25501         var cwhite = this.cwhite;
25502         var cblack = this.cblack;
25503             
25504         function cleanStyle(n,v)
25505         {
25506             if (v.match(/expression/)) { //XSS?? should we even bother..
25507                 node.removeAttribute(n);
25508                 return;
25509             }
25510             
25511             var parts = v.split(/;/);
25512             var clean = [];
25513             
25514             Roo.each(parts, function(p) {
25515                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25516                 if (!p.length) {
25517                     return true;
25518                 }
25519                 var l = p.split(':').shift().replace(/\s+/g,'');
25520                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25521                 
25522                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25523 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25524                     //node.removeAttribute(n);
25525                     return true;
25526                 }
25527                 //Roo.log()
25528                 // only allow 'c whitelisted system attributes'
25529                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25530 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25531                     //node.removeAttribute(n);
25532                     return true;
25533                 }
25534                 
25535                 
25536                  
25537                 
25538                 clean.push(p);
25539                 return true;
25540             });
25541             if (clean.length) { 
25542                 node.setAttribute(n, clean.join(';'));
25543             } else {
25544                 node.removeAttribute(n);
25545             }
25546             
25547         }
25548         
25549         
25550         for (var i = node.attributes.length-1; i > -1 ; i--) {
25551             var a = node.attributes[i];
25552             //console.log(a);
25553             
25554             if (a.name.toLowerCase().substr(0,2)=='on')  {
25555                 node.removeAttribute(a.name);
25556                 continue;
25557             }
25558             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25559                 node.removeAttribute(a.name);
25560                 continue;
25561             }
25562             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25563                 cleanAttr(a.name,a.value); // fixme..
25564                 continue;
25565             }
25566             if (a.name == 'style') {
25567                 cleanStyle(a.name,a.value);
25568                 continue;
25569             }
25570             /// clean up MS crap..
25571             // tecnically this should be a list of valid class'es..
25572             
25573             
25574             if (a.name == 'class') {
25575                 if (a.value.match(/^Mso/)) {
25576                     node.removeAttribute('class');
25577                 }
25578                 
25579                 if (a.value.match(/^body$/)) {
25580                     node.removeAttribute('class');
25581                 }
25582                 continue;
25583             }
25584             
25585             // style cleanup!?
25586             // class cleanup?
25587             
25588         }
25589         
25590         
25591         this.cleanUpChildren(node);
25592         
25593         
25594     },
25595     
25596     /**
25597      * Clean up MS wordisms...
25598      */
25599     cleanWord : function(node)
25600     {
25601         if (!node) {
25602             this.cleanWord(this.doc.body);
25603             return;
25604         }
25605         
25606         if(
25607                 node.nodeName == 'SPAN' &&
25608                 !node.hasAttributes() &&
25609                 node.childNodes.length == 1 &&
25610                 node.firstChild.nodeName == "#text"  
25611         ) {
25612             var textNode = node.firstChild;
25613             node.removeChild(textNode);
25614             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25615                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25616             }
25617             node.parentNode.insertBefore(textNode, node);
25618             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25619                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25620             }
25621             node.parentNode.removeChild(node);
25622         }
25623         
25624         if (node.nodeName == "#text") {
25625             // clean up silly Windows -- stuff?
25626             return; 
25627         }
25628         if (node.nodeName == "#comment") {
25629             node.parentNode.removeChild(node);
25630             // clean up silly Windows -- stuff?
25631             return; 
25632         }
25633         
25634         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25635             node.parentNode.removeChild(node);
25636             return;
25637         }
25638         //Roo.log(node.tagName);
25639         // remove - but keep children..
25640         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25641             //Roo.log('-- removed');
25642             while (node.childNodes.length) {
25643                 var cn = node.childNodes[0];
25644                 node.removeChild(cn);
25645                 node.parentNode.insertBefore(cn, node);
25646                 // move node to parent - and clean it..
25647                 this.cleanWord(cn);
25648             }
25649             node.parentNode.removeChild(node);
25650             /// no need to iterate chidlren = it's got none..
25651             //this.iterateChildren(node, this.cleanWord);
25652             return;
25653         }
25654         // clean styles
25655         if (node.className.length) {
25656             
25657             var cn = node.className.split(/\W+/);
25658             var cna = [];
25659             Roo.each(cn, function(cls) {
25660                 if (cls.match(/Mso[a-zA-Z]+/)) {
25661                     return;
25662                 }
25663                 cna.push(cls);
25664             });
25665             node.className = cna.length ? cna.join(' ') : '';
25666             if (!cna.length) {
25667                 node.removeAttribute("class");
25668             }
25669         }
25670         
25671         if (node.hasAttribute("lang")) {
25672             node.removeAttribute("lang");
25673         }
25674         
25675         if (node.hasAttribute("style")) {
25676             
25677             var styles = node.getAttribute("style").split(";");
25678             var nstyle = [];
25679             Roo.each(styles, function(s) {
25680                 if (!s.match(/:/)) {
25681                     return;
25682                 }
25683                 var kv = s.split(":");
25684                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25685                     return;
25686                 }
25687                 // what ever is left... we allow.
25688                 nstyle.push(s);
25689             });
25690             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25691             if (!nstyle.length) {
25692                 node.removeAttribute('style');
25693             }
25694         }
25695         this.iterateChildren(node, this.cleanWord);
25696         
25697         
25698         
25699     },
25700     /**
25701      * iterateChildren of a Node, calling fn each time, using this as the scole..
25702      * @param {DomNode} node node to iterate children of.
25703      * @param {Function} fn method of this class to call on each item.
25704      */
25705     iterateChildren : function(node, fn)
25706     {
25707         if (!node.childNodes.length) {
25708                 return;
25709         }
25710         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25711            fn.call(this, node.childNodes[i])
25712         }
25713     },
25714     
25715     
25716     /**
25717      * cleanTableWidths.
25718      *
25719      * Quite often pasting from word etc.. results in tables with column and widths.
25720      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25721      *
25722      */
25723     cleanTableWidths : function(node)
25724     {
25725          
25726          
25727         if (!node) {
25728             this.cleanTableWidths(this.doc.body);
25729             return;
25730         }
25731         
25732         // ignore list...
25733         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25734             return; 
25735         }
25736         Roo.log(node.tagName);
25737         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25738             this.iterateChildren(node, this.cleanTableWidths);
25739             return;
25740         }
25741         if (node.hasAttribute('width')) {
25742             node.removeAttribute('width');
25743         }
25744         
25745          
25746         if (node.hasAttribute("style")) {
25747             // pretty basic...
25748             
25749             var styles = node.getAttribute("style").split(";");
25750             var nstyle = [];
25751             Roo.each(styles, function(s) {
25752                 if (!s.match(/:/)) {
25753                     return;
25754                 }
25755                 var kv = s.split(":");
25756                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25757                     return;
25758                 }
25759                 // what ever is left... we allow.
25760                 nstyle.push(s);
25761             });
25762             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25763             if (!nstyle.length) {
25764                 node.removeAttribute('style');
25765             }
25766         }
25767         
25768         this.iterateChildren(node, this.cleanTableWidths);
25769         
25770         
25771     },
25772     
25773     
25774     
25775     
25776     domToHTML : function(currentElement, depth, nopadtext) {
25777         
25778         depth = depth || 0;
25779         nopadtext = nopadtext || false;
25780     
25781         if (!currentElement) {
25782             return this.domToHTML(this.doc.body);
25783         }
25784         
25785         //Roo.log(currentElement);
25786         var j;
25787         var allText = false;
25788         var nodeName = currentElement.nodeName;
25789         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25790         
25791         if  (nodeName == '#text') {
25792             
25793             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25794         }
25795         
25796         
25797         var ret = '';
25798         if (nodeName != 'BODY') {
25799              
25800             var i = 0;
25801             // Prints the node tagName, such as <A>, <IMG>, etc
25802             if (tagName) {
25803                 var attr = [];
25804                 for(i = 0; i < currentElement.attributes.length;i++) {
25805                     // quoting?
25806                     var aname = currentElement.attributes.item(i).name;
25807                     if (!currentElement.attributes.item(i).value.length) {
25808                         continue;
25809                     }
25810                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25811                 }
25812                 
25813                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25814             } 
25815             else {
25816                 
25817                 // eack
25818             }
25819         } else {
25820             tagName = false;
25821         }
25822         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25823             return ret;
25824         }
25825         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25826             nopadtext = true;
25827         }
25828         
25829         
25830         // Traverse the tree
25831         i = 0;
25832         var currentElementChild = currentElement.childNodes.item(i);
25833         var allText = true;
25834         var innerHTML  = '';
25835         lastnode = '';
25836         while (currentElementChild) {
25837             // Formatting code (indent the tree so it looks nice on the screen)
25838             var nopad = nopadtext;
25839             if (lastnode == 'SPAN') {
25840                 nopad  = true;
25841             }
25842             // text
25843             if  (currentElementChild.nodeName == '#text') {
25844                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25845                 toadd = nopadtext ? toadd : toadd.trim();
25846                 if (!nopad && toadd.length > 80) {
25847                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25848                 }
25849                 innerHTML  += toadd;
25850                 
25851                 i++;
25852                 currentElementChild = currentElement.childNodes.item(i);
25853                 lastNode = '';
25854                 continue;
25855             }
25856             allText = false;
25857             
25858             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25859                 
25860             // Recursively traverse the tree structure of the child node
25861             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25862             lastnode = currentElementChild.nodeName;
25863             i++;
25864             currentElementChild=currentElement.childNodes.item(i);
25865         }
25866         
25867         ret += innerHTML;
25868         
25869         if (!allText) {
25870                 // The remaining code is mostly for formatting the tree
25871             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25872         }
25873         
25874         
25875         if (tagName) {
25876             ret+= "</"+tagName+">";
25877         }
25878         return ret;
25879         
25880     },
25881         
25882     applyBlacklists : function()
25883     {
25884         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25885         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25886         
25887         this.white = [];
25888         this.black = [];
25889         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25890             if (b.indexOf(tag) > -1) {
25891                 return;
25892             }
25893             this.white.push(tag);
25894             
25895         }, this);
25896         
25897         Roo.each(w, function(tag) {
25898             if (b.indexOf(tag) > -1) {
25899                 return;
25900             }
25901             if (this.white.indexOf(tag) > -1) {
25902                 return;
25903             }
25904             this.white.push(tag);
25905             
25906         }, this);
25907         
25908         
25909         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25910             if (w.indexOf(tag) > -1) {
25911                 return;
25912             }
25913             this.black.push(tag);
25914             
25915         }, this);
25916         
25917         Roo.each(b, function(tag) {
25918             if (w.indexOf(tag) > -1) {
25919                 return;
25920             }
25921             if (this.black.indexOf(tag) > -1) {
25922                 return;
25923             }
25924             this.black.push(tag);
25925             
25926         }, this);
25927         
25928         
25929         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25930         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25931         
25932         this.cwhite = [];
25933         this.cblack = [];
25934         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25935             if (b.indexOf(tag) > -1) {
25936                 return;
25937             }
25938             this.cwhite.push(tag);
25939             
25940         }, this);
25941         
25942         Roo.each(w, function(tag) {
25943             if (b.indexOf(tag) > -1) {
25944                 return;
25945             }
25946             if (this.cwhite.indexOf(tag) > -1) {
25947                 return;
25948             }
25949             this.cwhite.push(tag);
25950             
25951         }, this);
25952         
25953         
25954         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25955             if (w.indexOf(tag) > -1) {
25956                 return;
25957             }
25958             this.cblack.push(tag);
25959             
25960         }, this);
25961         
25962         Roo.each(b, function(tag) {
25963             if (w.indexOf(tag) > -1) {
25964                 return;
25965             }
25966             if (this.cblack.indexOf(tag) > -1) {
25967                 return;
25968             }
25969             this.cblack.push(tag);
25970             
25971         }, this);
25972     },
25973     
25974     setStylesheets : function(stylesheets)
25975     {
25976         if(typeof(stylesheets) == 'string'){
25977             Roo.get(this.iframe.contentDocument.head).createChild({
25978                 tag : 'link',
25979                 rel : 'stylesheet',
25980                 type : 'text/css',
25981                 href : stylesheets
25982             });
25983             
25984             return;
25985         }
25986         var _this = this;
25987      
25988         Roo.each(stylesheets, function(s) {
25989             if(!s.length){
25990                 return;
25991             }
25992             
25993             Roo.get(_this.iframe.contentDocument.head).createChild({
25994                 tag : 'link',
25995                 rel : 'stylesheet',
25996                 type : 'text/css',
25997                 href : s
25998             });
25999         });
26000
26001         
26002     },
26003     
26004     removeStylesheets : function()
26005     {
26006         var _this = this;
26007         
26008         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26009             s.remove();
26010         });
26011     },
26012     
26013     setStyle : function(style)
26014     {
26015         Roo.get(this.iframe.contentDocument.head).createChild({
26016             tag : 'style',
26017             type : 'text/css',
26018             html : style
26019         });
26020
26021         return;
26022     }
26023     
26024     // hide stuff that is not compatible
26025     /**
26026      * @event blur
26027      * @hide
26028      */
26029     /**
26030      * @event change
26031      * @hide
26032      */
26033     /**
26034      * @event focus
26035      * @hide
26036      */
26037     /**
26038      * @event specialkey
26039      * @hide
26040      */
26041     /**
26042      * @cfg {String} fieldClass @hide
26043      */
26044     /**
26045      * @cfg {String} focusClass @hide
26046      */
26047     /**
26048      * @cfg {String} autoCreate @hide
26049      */
26050     /**
26051      * @cfg {String} inputType @hide
26052      */
26053     /**
26054      * @cfg {String} invalidClass @hide
26055      */
26056     /**
26057      * @cfg {String} invalidText @hide
26058      */
26059     /**
26060      * @cfg {String} msgFx @hide
26061      */
26062     /**
26063      * @cfg {String} validateOnBlur @hide
26064      */
26065 });
26066
26067 Roo.HtmlEditorCore.white = [
26068         'area', 'br', 'img', 'input', 'hr', 'wbr',
26069         
26070        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26071        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26072        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26073        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26074        'table',   'ul',         'xmp', 
26075        
26076        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26077       'thead',   'tr', 
26078      
26079       'dir', 'menu', 'ol', 'ul', 'dl',
26080        
26081       'embed',  'object'
26082 ];
26083
26084
26085 Roo.HtmlEditorCore.black = [
26086     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26087         'applet', // 
26088         'base',   'basefont', 'bgsound', 'blink',  'body', 
26089         'frame',  'frameset', 'head',    'html',   'ilayer', 
26090         'iframe', 'layer',  'link',     'meta',    'object',   
26091         'script', 'style' ,'title',  'xml' // clean later..
26092 ];
26093 Roo.HtmlEditorCore.clean = [
26094     'script', 'style', 'title', 'xml'
26095 ];
26096 Roo.HtmlEditorCore.remove = [
26097     'font'
26098 ];
26099 // attributes..
26100
26101 Roo.HtmlEditorCore.ablack = [
26102     'on'
26103 ];
26104     
26105 Roo.HtmlEditorCore.aclean = [ 
26106     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26107 ];
26108
26109 // protocols..
26110 Roo.HtmlEditorCore.pwhite= [
26111         'http',  'https',  'mailto'
26112 ];
26113
26114 // white listed style attributes.
26115 Roo.HtmlEditorCore.cwhite= [
26116       //  'text-align', /// default is to allow most things..
26117       
26118          
26119 //        'font-size'//??
26120 ];
26121
26122 // black listed style attributes.
26123 Roo.HtmlEditorCore.cblack= [
26124       //  'font-size' -- this can be set by the project 
26125 ];
26126
26127
26128 Roo.HtmlEditorCore.swapCodes   =[ 
26129     [    8211, "&#8211;" ], 
26130     [    8212, "&#8212;" ], 
26131     [    8216,  "'" ],  
26132     [    8217, "'" ],  
26133     [    8220, '"' ],  
26134     [    8221, '"' ],  
26135     [    8226, "*" ],  
26136     [    8230, "..." ]
26137 ]; 
26138
26139     /*
26140  * - LGPL
26141  *
26142  * HtmlEditor
26143  * 
26144  */
26145
26146 /**
26147  * @class Roo.bootstrap.HtmlEditor
26148  * @extends Roo.bootstrap.TextArea
26149  * Bootstrap HtmlEditor class
26150
26151  * @constructor
26152  * Create a new HtmlEditor
26153  * @param {Object} config The config object
26154  */
26155
26156 Roo.bootstrap.HtmlEditor = function(config){
26157     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26158     if (!this.toolbars) {
26159         this.toolbars = [];
26160     }
26161     
26162     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26163     this.addEvents({
26164             /**
26165              * @event initialize
26166              * Fires when the editor is fully initialized (including the iframe)
26167              * @param {HtmlEditor} this
26168              */
26169             initialize: true,
26170             /**
26171              * @event activate
26172              * Fires when the editor is first receives the focus. Any insertion must wait
26173              * until after this event.
26174              * @param {HtmlEditor} this
26175              */
26176             activate: true,
26177              /**
26178              * @event beforesync
26179              * Fires before the textarea is updated with content from the editor iframe. Return false
26180              * to cancel the sync.
26181              * @param {HtmlEditor} this
26182              * @param {String} html
26183              */
26184             beforesync: true,
26185              /**
26186              * @event beforepush
26187              * Fires before the iframe editor is updated with content from the textarea. Return false
26188              * to cancel the push.
26189              * @param {HtmlEditor} this
26190              * @param {String} html
26191              */
26192             beforepush: true,
26193              /**
26194              * @event sync
26195              * Fires when the textarea is updated with content from the editor iframe.
26196              * @param {HtmlEditor} this
26197              * @param {String} html
26198              */
26199             sync: true,
26200              /**
26201              * @event push
26202              * Fires when the iframe editor is updated with content from the textarea.
26203              * @param {HtmlEditor} this
26204              * @param {String} html
26205              */
26206             push: true,
26207              /**
26208              * @event editmodechange
26209              * Fires when the editor switches edit modes
26210              * @param {HtmlEditor} this
26211              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26212              */
26213             editmodechange: true,
26214             /**
26215              * @event editorevent
26216              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26217              * @param {HtmlEditor} this
26218              */
26219             editorevent: true,
26220             /**
26221              * @event firstfocus
26222              * Fires when on first focus - needed by toolbars..
26223              * @param {HtmlEditor} this
26224              */
26225             firstfocus: true,
26226             /**
26227              * @event autosave
26228              * Auto save the htmlEditor value as a file into Events
26229              * @param {HtmlEditor} this
26230              */
26231             autosave: true,
26232             /**
26233              * @event savedpreview
26234              * preview the saved version of htmlEditor
26235              * @param {HtmlEditor} this
26236              */
26237             savedpreview: true
26238         });
26239 };
26240
26241
26242 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26243     
26244     
26245       /**
26246      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26247      */
26248     toolbars : false,
26249     
26250      /**
26251     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26252     */
26253     btns : [],
26254    
26255      /**
26256      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26257      *                        Roo.resizable.
26258      */
26259     resizable : false,
26260      /**
26261      * @cfg {Number} height (in pixels)
26262      */   
26263     height: 300,
26264    /**
26265      * @cfg {Number} width (in pixels)
26266      */   
26267     width: false,
26268     
26269     /**
26270      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26271      * 
26272      */
26273     stylesheets: false,
26274     
26275     // id of frame..
26276     frameId: false,
26277     
26278     // private properties
26279     validationEvent : false,
26280     deferHeight: true,
26281     initialized : false,
26282     activated : false,
26283     
26284     onFocus : Roo.emptyFn,
26285     iframePad:3,
26286     hideMode:'offsets',
26287     
26288     tbContainer : false,
26289     
26290     bodyCls : '',
26291     
26292     toolbarContainer :function() {
26293         return this.wrap.select('.x-html-editor-tb',true).first();
26294     },
26295
26296     /**
26297      * Protected method that will not generally be called directly. It
26298      * is called when the editor creates its toolbar. Override this method if you need to
26299      * add custom toolbar buttons.
26300      * @param {HtmlEditor} editor
26301      */
26302     createToolbar : function(){
26303         Roo.log('renewing');
26304         Roo.log("create toolbars");
26305         
26306         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26307         this.toolbars[0].render(this.toolbarContainer());
26308         
26309         return;
26310         
26311 //        if (!editor.toolbars || !editor.toolbars.length) {
26312 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26313 //        }
26314 //        
26315 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26316 //            editor.toolbars[i] = Roo.factory(
26317 //                    typeof(editor.toolbars[i]) == 'string' ?
26318 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26319 //                Roo.bootstrap.HtmlEditor);
26320 //            editor.toolbars[i].init(editor);
26321 //        }
26322     },
26323
26324      
26325     // private
26326     onRender : function(ct, position)
26327     {
26328        // Roo.log("Call onRender: " + this.xtype);
26329         var _t = this;
26330         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26331       
26332         this.wrap = this.inputEl().wrap({
26333             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26334         });
26335         
26336         this.editorcore.onRender(ct, position);
26337          
26338         if (this.resizable) {
26339             this.resizeEl = new Roo.Resizable(this.wrap, {
26340                 pinned : true,
26341                 wrap: true,
26342                 dynamic : true,
26343                 minHeight : this.height,
26344                 height: this.height,
26345                 handles : this.resizable,
26346                 width: this.width,
26347                 listeners : {
26348                     resize : function(r, w, h) {
26349                         _t.onResize(w,h); // -something
26350                     }
26351                 }
26352             });
26353             
26354         }
26355         this.createToolbar(this);
26356        
26357         
26358         if(!this.width && this.resizable){
26359             this.setSize(this.wrap.getSize());
26360         }
26361         if (this.resizeEl) {
26362             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26363             // should trigger onReize..
26364         }
26365         
26366     },
26367
26368     // private
26369     onResize : function(w, h)
26370     {
26371         Roo.log('resize: ' +w + ',' + h );
26372         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26373         var ew = false;
26374         var eh = false;
26375         
26376         if(this.inputEl() ){
26377             if(typeof w == 'number'){
26378                 var aw = w - this.wrap.getFrameWidth('lr');
26379                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26380                 ew = aw;
26381             }
26382             if(typeof h == 'number'){
26383                  var tbh = -11;  // fixme it needs to tool bar size!
26384                 for (var i =0; i < this.toolbars.length;i++) {
26385                     // fixme - ask toolbars for heights?
26386                     tbh += this.toolbars[i].el.getHeight();
26387                     //if (this.toolbars[i].footer) {
26388                     //    tbh += this.toolbars[i].footer.el.getHeight();
26389                     //}
26390                 }
26391               
26392                 
26393                 
26394                 
26395                 
26396                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26397                 ah -= 5; // knock a few pixes off for look..
26398                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26399                 var eh = ah;
26400             }
26401         }
26402         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26403         this.editorcore.onResize(ew,eh);
26404         
26405     },
26406
26407     /**
26408      * Toggles the editor between standard and source edit mode.
26409      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26410      */
26411     toggleSourceEdit : function(sourceEditMode)
26412     {
26413         this.editorcore.toggleSourceEdit(sourceEditMode);
26414         
26415         if(this.editorcore.sourceEditMode){
26416             Roo.log('editor - showing textarea');
26417             
26418 //            Roo.log('in');
26419 //            Roo.log(this.syncValue());
26420             this.syncValue();
26421             this.inputEl().removeClass(['hide', 'x-hidden']);
26422             this.inputEl().dom.removeAttribute('tabIndex');
26423             this.inputEl().focus();
26424         }else{
26425             Roo.log('editor - hiding textarea');
26426 //            Roo.log('out')
26427 //            Roo.log(this.pushValue()); 
26428             this.pushValue();
26429             
26430             this.inputEl().addClass(['hide', 'x-hidden']);
26431             this.inputEl().dom.setAttribute('tabIndex', -1);
26432             //this.deferFocus();
26433         }
26434          
26435         if(this.resizable){
26436             this.setSize(this.wrap.getSize());
26437         }
26438         
26439         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26440     },
26441  
26442     // private (for BoxComponent)
26443     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26444
26445     // private (for BoxComponent)
26446     getResizeEl : function(){
26447         return this.wrap;
26448     },
26449
26450     // private (for BoxComponent)
26451     getPositionEl : function(){
26452         return this.wrap;
26453     },
26454
26455     // private
26456     initEvents : function(){
26457         this.originalValue = this.getValue();
26458     },
26459
26460 //    /**
26461 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26462 //     * @method
26463 //     */
26464 //    markInvalid : Roo.emptyFn,
26465 //    /**
26466 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26467 //     * @method
26468 //     */
26469 //    clearInvalid : Roo.emptyFn,
26470
26471     setValue : function(v){
26472         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26473         this.editorcore.pushValue();
26474     },
26475
26476      
26477     // private
26478     deferFocus : function(){
26479         this.focus.defer(10, this);
26480     },
26481
26482     // doc'ed in Field
26483     focus : function(){
26484         this.editorcore.focus();
26485         
26486     },
26487       
26488
26489     // private
26490     onDestroy : function(){
26491         
26492         
26493         
26494         if(this.rendered){
26495             
26496             for (var i =0; i < this.toolbars.length;i++) {
26497                 // fixme - ask toolbars for heights?
26498                 this.toolbars[i].onDestroy();
26499             }
26500             
26501             this.wrap.dom.innerHTML = '';
26502             this.wrap.remove();
26503         }
26504     },
26505
26506     // private
26507     onFirstFocus : function(){
26508         //Roo.log("onFirstFocus");
26509         this.editorcore.onFirstFocus();
26510          for (var i =0; i < this.toolbars.length;i++) {
26511             this.toolbars[i].onFirstFocus();
26512         }
26513         
26514     },
26515     
26516     // private
26517     syncValue : function()
26518     {   
26519         this.editorcore.syncValue();
26520     },
26521     
26522     pushValue : function()
26523     {   
26524         this.editorcore.pushValue();
26525     }
26526      
26527     
26528     // hide stuff that is not compatible
26529     /**
26530      * @event blur
26531      * @hide
26532      */
26533     /**
26534      * @event change
26535      * @hide
26536      */
26537     /**
26538      * @event focus
26539      * @hide
26540      */
26541     /**
26542      * @event specialkey
26543      * @hide
26544      */
26545     /**
26546      * @cfg {String} fieldClass @hide
26547      */
26548     /**
26549      * @cfg {String} focusClass @hide
26550      */
26551     /**
26552      * @cfg {String} autoCreate @hide
26553      */
26554     /**
26555      * @cfg {String} inputType @hide
26556      */
26557      
26558     /**
26559      * @cfg {String} invalidText @hide
26560      */
26561     /**
26562      * @cfg {String} msgFx @hide
26563      */
26564     /**
26565      * @cfg {String} validateOnBlur @hide
26566      */
26567 });
26568  
26569     
26570    
26571    
26572    
26573       
26574 Roo.namespace('Roo.bootstrap.htmleditor');
26575 /**
26576  * @class Roo.bootstrap.HtmlEditorToolbar1
26577  * Basic Toolbar
26578  * 
26579  * @example
26580  * Usage:
26581  *
26582  new Roo.bootstrap.HtmlEditor({
26583     ....
26584     toolbars : [
26585         new Roo.bootstrap.HtmlEditorToolbar1({
26586             disable : { fonts: 1 , format: 1, ..., ... , ...],
26587             btns : [ .... ]
26588         })
26589     }
26590      
26591  * 
26592  * @cfg {Object} disable List of elements to disable..
26593  * @cfg {Array} btns List of additional buttons.
26594  * 
26595  * 
26596  * NEEDS Extra CSS? 
26597  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26598  */
26599  
26600 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26601 {
26602     
26603     Roo.apply(this, config);
26604     
26605     // default disabled, based on 'good practice'..
26606     this.disable = this.disable || {};
26607     Roo.applyIf(this.disable, {
26608         fontSize : true,
26609         colors : true,
26610         specialElements : true
26611     });
26612     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26613     
26614     this.editor = config.editor;
26615     this.editorcore = config.editor.editorcore;
26616     
26617     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26618     
26619     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26620     // dont call parent... till later.
26621 }
26622 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26623      
26624     bar : true,
26625     
26626     editor : false,
26627     editorcore : false,
26628     
26629     
26630     formats : [
26631         "p" ,  
26632         "h1","h2","h3","h4","h5","h6", 
26633         "pre", "code", 
26634         "abbr", "acronym", "address", "cite", "samp", "var",
26635         'div','span'
26636     ],
26637     
26638     onRender : function(ct, position)
26639     {
26640        // Roo.log("Call onRender: " + this.xtype);
26641         
26642        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26643        Roo.log(this.el);
26644        this.el.dom.style.marginBottom = '0';
26645        var _this = this;
26646        var editorcore = this.editorcore;
26647        var editor= this.editor;
26648        
26649        var children = [];
26650        var btn = function(id,cmd , toggle, handler, html){
26651        
26652             var  event = toggle ? 'toggle' : 'click';
26653        
26654             var a = {
26655                 size : 'sm',
26656                 xtype: 'Button',
26657                 xns: Roo.bootstrap,
26658                 //glyphicon : id,
26659                 fa: id,
26660                 cmd : id || cmd,
26661                 enableToggle:toggle !== false,
26662                 html : html || '',
26663                 pressed : toggle ? false : null,
26664                 listeners : {}
26665             };
26666             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26667                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26668             };
26669             children.push(a);
26670             return a;
26671        }
26672        
26673     //    var cb_box = function...
26674         
26675         var style = {
26676                 xtype: 'Button',
26677                 size : 'sm',
26678                 xns: Roo.bootstrap,
26679                 fa : 'font',
26680                 //html : 'submit'
26681                 menu : {
26682                     xtype: 'Menu',
26683                     xns: Roo.bootstrap,
26684                     items:  []
26685                 }
26686         };
26687         Roo.each(this.formats, function(f) {
26688             style.menu.items.push({
26689                 xtype :'MenuItem',
26690                 xns: Roo.bootstrap,
26691                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26692                 tagname : f,
26693                 listeners : {
26694                     click : function()
26695                     {
26696                         editorcore.insertTag(this.tagname);
26697                         editor.focus();
26698                     }
26699                 }
26700                 
26701             });
26702         });
26703         children.push(style);   
26704         
26705         btn('bold',false,true);
26706         btn('italic',false,true);
26707         btn('align-left', 'justifyleft',true);
26708         btn('align-center', 'justifycenter',true);
26709         btn('align-right' , 'justifyright',true);
26710         btn('link', false, false, function(btn) {
26711             //Roo.log("create link?");
26712             var url = prompt(this.createLinkText, this.defaultLinkValue);
26713             if(url && url != 'http:/'+'/'){
26714                 this.editorcore.relayCmd('createlink', url);
26715             }
26716         }),
26717         btn('list','insertunorderedlist',true);
26718         btn('pencil', false,true, function(btn){
26719                 Roo.log(this);
26720                 this.toggleSourceEdit(btn.pressed);
26721         });
26722         
26723         if (this.editor.btns.length > 0) {
26724             for (var i = 0; i<this.editor.btns.length; i++) {
26725                 children.push(this.editor.btns[i]);
26726             }
26727         }
26728         
26729         /*
26730         var cog = {
26731                 xtype: 'Button',
26732                 size : 'sm',
26733                 xns: Roo.bootstrap,
26734                 glyphicon : 'cog',
26735                 //html : 'submit'
26736                 menu : {
26737                     xtype: 'Menu',
26738                     xns: Roo.bootstrap,
26739                     items:  []
26740                 }
26741         };
26742         
26743         cog.menu.items.push({
26744             xtype :'MenuItem',
26745             xns: Roo.bootstrap,
26746             html : Clean styles,
26747             tagname : f,
26748             listeners : {
26749                 click : function()
26750                 {
26751                     editorcore.insertTag(this.tagname);
26752                     editor.focus();
26753                 }
26754             }
26755             
26756         });
26757        */
26758         
26759          
26760        this.xtype = 'NavSimplebar';
26761         
26762         for(var i=0;i< children.length;i++) {
26763             
26764             this.buttons.add(this.addxtypeChild(children[i]));
26765             
26766         }
26767         
26768         editor.on('editorevent', this.updateToolbar, this);
26769     },
26770     onBtnClick : function(id)
26771     {
26772        this.editorcore.relayCmd(id);
26773        this.editorcore.focus();
26774     },
26775     
26776     /**
26777      * Protected method that will not generally be called directly. It triggers
26778      * a toolbar update by reading the markup state of the current selection in the editor.
26779      */
26780     updateToolbar: function(){
26781
26782         if(!this.editorcore.activated){
26783             this.editor.onFirstFocus(); // is this neeed?
26784             return;
26785         }
26786
26787         var btns = this.buttons; 
26788         var doc = this.editorcore.doc;
26789         btns.get('bold').setActive(doc.queryCommandState('bold'));
26790         btns.get('italic').setActive(doc.queryCommandState('italic'));
26791         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26792         
26793         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26794         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26795         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26796         
26797         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26798         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26799          /*
26800         
26801         var ans = this.editorcore.getAllAncestors();
26802         if (this.formatCombo) {
26803             
26804             
26805             var store = this.formatCombo.store;
26806             this.formatCombo.setValue("");
26807             for (var i =0; i < ans.length;i++) {
26808                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26809                     // select it..
26810                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26811                     break;
26812                 }
26813             }
26814         }
26815         
26816         
26817         
26818         // hides menus... - so this cant be on a menu...
26819         Roo.bootstrap.MenuMgr.hideAll();
26820         */
26821         Roo.bootstrap.MenuMgr.hideAll();
26822         //this.editorsyncValue();
26823     },
26824     onFirstFocus: function() {
26825         this.buttons.each(function(item){
26826            item.enable();
26827         });
26828     },
26829     toggleSourceEdit : function(sourceEditMode){
26830         
26831           
26832         if(sourceEditMode){
26833             Roo.log("disabling buttons");
26834            this.buttons.each( function(item){
26835                 if(item.cmd != 'pencil'){
26836                     item.disable();
26837                 }
26838             });
26839           
26840         }else{
26841             Roo.log("enabling buttons");
26842             if(this.editorcore.initialized){
26843                 this.buttons.each( function(item){
26844                     item.enable();
26845                 });
26846             }
26847             
26848         }
26849         Roo.log("calling toggole on editor");
26850         // tell the editor that it's been pressed..
26851         this.editor.toggleSourceEdit(sourceEditMode);
26852        
26853     }
26854 });
26855
26856
26857
26858
26859  
26860 /*
26861  * - LGPL
26862  */
26863
26864 /**
26865  * @class Roo.bootstrap.Markdown
26866  * @extends Roo.bootstrap.TextArea
26867  * Bootstrap Showdown editable area
26868  * @cfg {string} content
26869  * 
26870  * @constructor
26871  * Create a new Showdown
26872  */
26873
26874 Roo.bootstrap.Markdown = function(config){
26875     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26876    
26877 };
26878
26879 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26880     
26881     editing :false,
26882     
26883     initEvents : function()
26884     {
26885         
26886         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26887         this.markdownEl = this.el.createChild({
26888             cls : 'roo-markdown-area'
26889         });
26890         this.inputEl().addClass('d-none');
26891         if (this.getValue() == '') {
26892             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26893             
26894         } else {
26895             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26896         }
26897         this.markdownEl.on('click', this.toggleTextEdit, this);
26898         this.on('blur', this.toggleTextEdit, this);
26899         this.on('specialkey', this.resizeTextArea, this);
26900     },
26901     
26902     toggleTextEdit : function()
26903     {
26904         var sh = this.markdownEl.getHeight();
26905         this.inputEl().addClass('d-none');
26906         this.markdownEl.addClass('d-none');
26907         if (!this.editing) {
26908             // show editor?
26909             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26910             this.inputEl().removeClass('d-none');
26911             this.inputEl().focus();
26912             this.editing = true;
26913             return;
26914         }
26915         // show showdown...
26916         this.updateMarkdown();
26917         this.markdownEl.removeClass('d-none');
26918         this.editing = false;
26919         return;
26920     },
26921     updateMarkdown : function()
26922     {
26923         if (this.getValue() == '') {
26924             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26925             return;
26926         }
26927  
26928         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26929     },
26930     
26931     resizeTextArea: function () {
26932         
26933         var sh = 100;
26934         Roo.log([sh, this.getValue().split("\n").length * 30]);
26935         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26936     },
26937     setValue : function(val)
26938     {
26939         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26940         if (!this.editing) {
26941             this.updateMarkdown();
26942         }
26943         
26944     },
26945     focus : function()
26946     {
26947         if (!this.editing) {
26948             this.toggleTextEdit();
26949         }
26950         
26951     }
26952
26953
26954 });
26955 /**
26956  * @class Roo.bootstrap.Table.AbstractSelectionModel
26957  * @extends Roo.util.Observable
26958  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26959  * implemented by descendant classes.  This class should not be directly instantiated.
26960  * @constructor
26961  */
26962 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26963     this.locked = false;
26964     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26965 };
26966
26967
26968 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26969     /** @ignore Called by the grid automatically. Do not call directly. */
26970     init : function(grid){
26971         this.grid = grid;
26972         this.initEvents();
26973     },
26974
26975     /**
26976      * Locks the selections.
26977      */
26978     lock : function(){
26979         this.locked = true;
26980     },
26981
26982     /**
26983      * Unlocks the selections.
26984      */
26985     unlock : function(){
26986         this.locked = false;
26987     },
26988
26989     /**
26990      * Returns true if the selections are locked.
26991      * @return {Boolean}
26992      */
26993     isLocked : function(){
26994         return this.locked;
26995     },
26996     
26997     
26998     initEvents : function ()
26999     {
27000         
27001     }
27002 });
27003 /**
27004  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27005  * @class Roo.bootstrap.Table.RowSelectionModel
27006  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27007  * It supports multiple selections and keyboard selection/navigation. 
27008  * @constructor
27009  * @param {Object} config
27010  */
27011
27012 Roo.bootstrap.Table.RowSelectionModel = function(config){
27013     Roo.apply(this, config);
27014     this.selections = new Roo.util.MixedCollection(false, function(o){
27015         return o.id;
27016     });
27017
27018     this.last = false;
27019     this.lastActive = false;
27020
27021     this.addEvents({
27022         /**
27023              * @event selectionchange
27024              * Fires when the selection changes
27025              * @param {SelectionModel} this
27026              */
27027             "selectionchange" : true,
27028         /**
27029              * @event afterselectionchange
27030              * Fires after the selection changes (eg. by key press or clicking)
27031              * @param {SelectionModel} this
27032              */
27033             "afterselectionchange" : true,
27034         /**
27035              * @event beforerowselect
27036              * Fires when a row is selected being selected, return false to cancel.
27037              * @param {SelectionModel} this
27038              * @param {Number} rowIndex The selected index
27039              * @param {Boolean} keepExisting False if other selections will be cleared
27040              */
27041             "beforerowselect" : true,
27042         /**
27043              * @event rowselect
27044              * Fires when a row is selected.
27045              * @param {SelectionModel} this
27046              * @param {Number} rowIndex The selected index
27047              * @param {Roo.data.Record} r The record
27048              */
27049             "rowselect" : true,
27050         /**
27051              * @event rowdeselect
27052              * Fires when a row is deselected.
27053              * @param {SelectionModel} this
27054              * @param {Number} rowIndex The selected index
27055              */
27056         "rowdeselect" : true
27057     });
27058     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27059     this.locked = false;
27060  };
27061
27062 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27063     /**
27064      * @cfg {Boolean} singleSelect
27065      * True to allow selection of only one row at a time (defaults to false)
27066      */
27067     singleSelect : false,
27068
27069     // private
27070     initEvents : function()
27071     {
27072
27073         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27074         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27075         //}else{ // allow click to work like normal
27076          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27077         //}
27078         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27079         this.grid.on("rowclick", this.handleMouseDown, this);
27080         
27081         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27082             "up" : function(e){
27083                 if(!e.shiftKey){
27084                     this.selectPrevious(e.shiftKey);
27085                 }else if(this.last !== false && this.lastActive !== false){
27086                     var last = this.last;
27087                     this.selectRange(this.last,  this.lastActive-1);
27088                     this.grid.getView().focusRow(this.lastActive);
27089                     if(last !== false){
27090                         this.last = last;
27091                     }
27092                 }else{
27093                     this.selectFirstRow();
27094                 }
27095                 this.fireEvent("afterselectionchange", this);
27096             },
27097             "down" : function(e){
27098                 if(!e.shiftKey){
27099                     this.selectNext(e.shiftKey);
27100                 }else if(this.last !== false && this.lastActive !== false){
27101                     var last = this.last;
27102                     this.selectRange(this.last,  this.lastActive+1);
27103                     this.grid.getView().focusRow(this.lastActive);
27104                     if(last !== false){
27105                         this.last = last;
27106                     }
27107                 }else{
27108                     this.selectFirstRow();
27109                 }
27110                 this.fireEvent("afterselectionchange", this);
27111             },
27112             scope: this
27113         });
27114         this.grid.store.on('load', function(){
27115             this.selections.clear();
27116         },this);
27117         /*
27118         var view = this.grid.view;
27119         view.on("refresh", this.onRefresh, this);
27120         view.on("rowupdated", this.onRowUpdated, this);
27121         view.on("rowremoved", this.onRemove, this);
27122         */
27123     },
27124
27125     // private
27126     onRefresh : function()
27127     {
27128         var ds = this.grid.store, i, v = this.grid.view;
27129         var s = this.selections;
27130         s.each(function(r){
27131             if((i = ds.indexOfId(r.id)) != -1){
27132                 v.onRowSelect(i);
27133             }else{
27134                 s.remove(r);
27135             }
27136         });
27137     },
27138
27139     // private
27140     onRemove : function(v, index, r){
27141         this.selections.remove(r);
27142     },
27143
27144     // private
27145     onRowUpdated : function(v, index, r){
27146         if(this.isSelected(r)){
27147             v.onRowSelect(index);
27148         }
27149     },
27150
27151     /**
27152      * Select records.
27153      * @param {Array} records The records to select
27154      * @param {Boolean} keepExisting (optional) True to keep existing selections
27155      */
27156     selectRecords : function(records, keepExisting)
27157     {
27158         if(!keepExisting){
27159             this.clearSelections();
27160         }
27161             var ds = this.grid.store;
27162         for(var i = 0, len = records.length; i < len; i++){
27163             this.selectRow(ds.indexOf(records[i]), true);
27164         }
27165     },
27166
27167     /**
27168      * Gets the number of selected rows.
27169      * @return {Number}
27170      */
27171     getCount : function(){
27172         return this.selections.length;
27173     },
27174
27175     /**
27176      * Selects the first row in the grid.
27177      */
27178     selectFirstRow : function(){
27179         this.selectRow(0);
27180     },
27181
27182     /**
27183      * Select the last row.
27184      * @param {Boolean} keepExisting (optional) True to keep existing selections
27185      */
27186     selectLastRow : function(keepExisting){
27187         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27188         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27189     },
27190
27191     /**
27192      * Selects the row immediately following the last selected row.
27193      * @param {Boolean} keepExisting (optional) True to keep existing selections
27194      */
27195     selectNext : function(keepExisting)
27196     {
27197             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27198             this.selectRow(this.last+1, keepExisting);
27199             this.grid.getView().focusRow(this.last);
27200         }
27201     },
27202
27203     /**
27204      * Selects the row that precedes the last selected row.
27205      * @param {Boolean} keepExisting (optional) True to keep existing selections
27206      */
27207     selectPrevious : function(keepExisting){
27208         if(this.last){
27209             this.selectRow(this.last-1, keepExisting);
27210             this.grid.getView().focusRow(this.last);
27211         }
27212     },
27213
27214     /**
27215      * Returns the selected records
27216      * @return {Array} Array of selected records
27217      */
27218     getSelections : function(){
27219         return [].concat(this.selections.items);
27220     },
27221
27222     /**
27223      * Returns the first selected record.
27224      * @return {Record}
27225      */
27226     getSelected : function(){
27227         return this.selections.itemAt(0);
27228     },
27229
27230
27231     /**
27232      * Clears all selections.
27233      */
27234     clearSelections : function(fast)
27235     {
27236         if(this.locked) {
27237             return;
27238         }
27239         if(fast !== true){
27240                 var ds = this.grid.store;
27241             var s = this.selections;
27242             s.each(function(r){
27243                 this.deselectRow(ds.indexOfId(r.id));
27244             }, this);
27245             s.clear();
27246         }else{
27247             this.selections.clear();
27248         }
27249         this.last = false;
27250     },
27251
27252
27253     /**
27254      * Selects all rows.
27255      */
27256     selectAll : function(){
27257         if(this.locked) {
27258             return;
27259         }
27260         this.selections.clear();
27261         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27262             this.selectRow(i, true);
27263         }
27264     },
27265
27266     /**
27267      * Returns True if there is a selection.
27268      * @return {Boolean}
27269      */
27270     hasSelection : function(){
27271         return this.selections.length > 0;
27272     },
27273
27274     /**
27275      * Returns True if the specified row is selected.
27276      * @param {Number/Record} record The record or index of the record to check
27277      * @return {Boolean}
27278      */
27279     isSelected : function(index){
27280             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27281         return (r && this.selections.key(r.id) ? true : false);
27282     },
27283
27284     /**
27285      * Returns True if the specified record id is selected.
27286      * @param {String} id The id of record to check
27287      * @return {Boolean}
27288      */
27289     isIdSelected : function(id){
27290         return (this.selections.key(id) ? true : false);
27291     },
27292
27293
27294     // private
27295     handleMouseDBClick : function(e, t){
27296         
27297     },
27298     // private
27299     handleMouseDown : function(e, t)
27300     {
27301             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27302         if(this.isLocked() || rowIndex < 0 ){
27303             return;
27304         };
27305         if(e.shiftKey && this.last !== false){
27306             var last = this.last;
27307             this.selectRange(last, rowIndex, e.ctrlKey);
27308             this.last = last; // reset the last
27309             t.focus();
27310     
27311         }else{
27312             var isSelected = this.isSelected(rowIndex);
27313             //Roo.log("select row:" + rowIndex);
27314             if(isSelected){
27315                 this.deselectRow(rowIndex);
27316             } else {
27317                         this.selectRow(rowIndex, true);
27318             }
27319     
27320             /*
27321                 if(e.button !== 0 && isSelected){
27322                 alert('rowIndex 2: ' + rowIndex);
27323                     view.focusRow(rowIndex);
27324                 }else if(e.ctrlKey && isSelected){
27325                     this.deselectRow(rowIndex);
27326                 }else if(!isSelected){
27327                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27328                     view.focusRow(rowIndex);
27329                 }
27330             */
27331         }
27332         this.fireEvent("afterselectionchange", this);
27333     },
27334     // private
27335     handleDragableRowClick :  function(grid, rowIndex, e) 
27336     {
27337         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27338             this.selectRow(rowIndex, false);
27339             grid.view.focusRow(rowIndex);
27340              this.fireEvent("afterselectionchange", this);
27341         }
27342     },
27343     
27344     /**
27345      * Selects multiple rows.
27346      * @param {Array} rows Array of the indexes of the row to select
27347      * @param {Boolean} keepExisting (optional) True to keep existing selections
27348      */
27349     selectRows : function(rows, keepExisting){
27350         if(!keepExisting){
27351             this.clearSelections();
27352         }
27353         for(var i = 0, len = rows.length; i < len; i++){
27354             this.selectRow(rows[i], true);
27355         }
27356     },
27357
27358     /**
27359      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27360      * @param {Number} startRow The index of the first row in the range
27361      * @param {Number} endRow The index of the last row in the range
27362      * @param {Boolean} keepExisting (optional) True to retain existing selections
27363      */
27364     selectRange : function(startRow, endRow, keepExisting){
27365         if(this.locked) {
27366             return;
27367         }
27368         if(!keepExisting){
27369             this.clearSelections();
27370         }
27371         if(startRow <= endRow){
27372             for(var i = startRow; i <= endRow; i++){
27373                 this.selectRow(i, true);
27374             }
27375         }else{
27376             for(var i = startRow; i >= endRow; i--){
27377                 this.selectRow(i, true);
27378             }
27379         }
27380     },
27381
27382     /**
27383      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27384      * @param {Number} startRow The index of the first row in the range
27385      * @param {Number} endRow The index of the last row in the range
27386      */
27387     deselectRange : function(startRow, endRow, preventViewNotify){
27388         if(this.locked) {
27389             return;
27390         }
27391         for(var i = startRow; i <= endRow; i++){
27392             this.deselectRow(i, preventViewNotify);
27393         }
27394     },
27395
27396     /**
27397      * Selects a row.
27398      * @param {Number} row The index of the row to select
27399      * @param {Boolean} keepExisting (optional) True to keep existing selections
27400      */
27401     selectRow : function(index, keepExisting, preventViewNotify)
27402     {
27403             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27404             return;
27405         }
27406         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27407             if(!keepExisting || this.singleSelect){
27408                 this.clearSelections();
27409             }
27410             
27411             var r = this.grid.store.getAt(index);
27412             //console.log('selectRow - record id :' + r.id);
27413             
27414             this.selections.add(r);
27415             this.last = this.lastActive = index;
27416             if(!preventViewNotify){
27417                 var proxy = new Roo.Element(
27418                                 this.grid.getRowDom(index)
27419                 );
27420                 proxy.addClass('bg-info info');
27421             }
27422             this.fireEvent("rowselect", this, index, r);
27423             this.fireEvent("selectionchange", this);
27424         }
27425     },
27426
27427     /**
27428      * Deselects a row.
27429      * @param {Number} row The index of the row to deselect
27430      */
27431     deselectRow : function(index, preventViewNotify)
27432     {
27433         if(this.locked) {
27434             return;
27435         }
27436         if(this.last == index){
27437             this.last = false;
27438         }
27439         if(this.lastActive == index){
27440             this.lastActive = false;
27441         }
27442         
27443         var r = this.grid.store.getAt(index);
27444         if (!r) {
27445             return;
27446         }
27447         
27448         this.selections.remove(r);
27449         //.console.log('deselectRow - record id :' + r.id);
27450         if(!preventViewNotify){
27451         
27452             var proxy = new Roo.Element(
27453                 this.grid.getRowDom(index)
27454             );
27455             proxy.removeClass('bg-info info');
27456         }
27457         this.fireEvent("rowdeselect", this, index);
27458         this.fireEvent("selectionchange", this);
27459     },
27460
27461     // private
27462     restoreLast : function(){
27463         if(this._last){
27464             this.last = this._last;
27465         }
27466     },
27467
27468     // private
27469     acceptsNav : function(row, col, cm){
27470         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27471     },
27472
27473     // private
27474     onEditorKey : function(field, e){
27475         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27476         if(k == e.TAB){
27477             e.stopEvent();
27478             ed.completeEdit();
27479             if(e.shiftKey){
27480                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27481             }else{
27482                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27483             }
27484         }else if(k == e.ENTER && !e.ctrlKey){
27485             e.stopEvent();
27486             ed.completeEdit();
27487             if(e.shiftKey){
27488                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27489             }else{
27490                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27491             }
27492         }else if(k == e.ESC){
27493             ed.cancelEdit();
27494         }
27495         if(newCell){
27496             g.startEditing(newCell[0], newCell[1]);
27497         }
27498     }
27499 });
27500 /*
27501  * Based on:
27502  * Ext JS Library 1.1.1
27503  * Copyright(c) 2006-2007, Ext JS, LLC.
27504  *
27505  * Originally Released Under LGPL - original licence link has changed is not relivant.
27506  *
27507  * Fork - LGPL
27508  * <script type="text/javascript">
27509  */
27510  
27511 /**
27512  * @class Roo.bootstrap.PagingToolbar
27513  * @extends Roo.bootstrap.NavSimplebar
27514  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27515  * @constructor
27516  * Create a new PagingToolbar
27517  * @param {Object} config The config object
27518  * @param {Roo.data.Store} store
27519  */
27520 Roo.bootstrap.PagingToolbar = function(config)
27521 {
27522     // old args format still supported... - xtype is prefered..
27523         // created from xtype...
27524     
27525     this.ds = config.dataSource;
27526     
27527     if (config.store && !this.ds) {
27528         this.store= Roo.factory(config.store, Roo.data);
27529         this.ds = this.store;
27530         this.ds.xmodule = this.xmodule || false;
27531     }
27532     
27533     this.toolbarItems = [];
27534     if (config.items) {
27535         this.toolbarItems = config.items;
27536     }
27537     
27538     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27539     
27540     this.cursor = 0;
27541     
27542     if (this.ds) { 
27543         this.bind(this.ds);
27544     }
27545     
27546     if (Roo.bootstrap.version == 4) {
27547         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27548     } else {
27549         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27550     }
27551     
27552 };
27553
27554 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27555     /**
27556      * @cfg {Roo.data.Store} dataSource
27557      * The underlying data store providing the paged data
27558      */
27559     /**
27560      * @cfg {String/HTMLElement/Element} container
27561      * container The id or element that will contain the toolbar
27562      */
27563     /**
27564      * @cfg {Boolean} displayInfo
27565      * True to display the displayMsg (defaults to false)
27566      */
27567     /**
27568      * @cfg {Number} pageSize
27569      * The number of records to display per page (defaults to 20)
27570      */
27571     pageSize: 20,
27572     /**
27573      * @cfg {String} displayMsg
27574      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27575      */
27576     displayMsg : 'Displaying {0} - {1} of {2}',
27577     /**
27578      * @cfg {String} emptyMsg
27579      * The message to display when no records are found (defaults to "No data to display")
27580      */
27581     emptyMsg : 'No data to display',
27582     /**
27583      * Customizable piece of the default paging text (defaults to "Page")
27584      * @type String
27585      */
27586     beforePageText : "Page",
27587     /**
27588      * Customizable piece of the default paging text (defaults to "of %0")
27589      * @type String
27590      */
27591     afterPageText : "of {0}",
27592     /**
27593      * Customizable piece of the default paging text (defaults to "First Page")
27594      * @type String
27595      */
27596     firstText : "First Page",
27597     /**
27598      * Customizable piece of the default paging text (defaults to "Previous Page")
27599      * @type String
27600      */
27601     prevText : "Previous Page",
27602     /**
27603      * Customizable piece of the default paging text (defaults to "Next Page")
27604      * @type String
27605      */
27606     nextText : "Next Page",
27607     /**
27608      * Customizable piece of the default paging text (defaults to "Last Page")
27609      * @type String
27610      */
27611     lastText : "Last Page",
27612     /**
27613      * Customizable piece of the default paging text (defaults to "Refresh")
27614      * @type String
27615      */
27616     refreshText : "Refresh",
27617
27618     buttons : false,
27619     // private
27620     onRender : function(ct, position) 
27621     {
27622         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27623         this.navgroup.parentId = this.id;
27624         this.navgroup.onRender(this.el, null);
27625         // add the buttons to the navgroup
27626         
27627         if(this.displayInfo){
27628             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27629             this.displayEl = this.el.select('.x-paging-info', true).first();
27630 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27631 //            this.displayEl = navel.el.select('span',true).first();
27632         }
27633         
27634         var _this = this;
27635         
27636         if(this.buttons){
27637             Roo.each(_this.buttons, function(e){ // this might need to use render????
27638                Roo.factory(e).render(_this.el);
27639             });
27640         }
27641             
27642         Roo.each(_this.toolbarItems, function(e) {
27643             _this.navgroup.addItem(e);
27644         });
27645         
27646         
27647         this.first = this.navgroup.addItem({
27648             tooltip: this.firstText,
27649             cls: "prev btn-outline-secondary",
27650             html : ' <i class="fa fa-step-backward"></i>',
27651             disabled: true,
27652             preventDefault: true,
27653             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27654         });
27655         
27656         this.prev =  this.navgroup.addItem({
27657             tooltip: this.prevText,
27658             cls: "prev btn-outline-secondary",
27659             html : ' <i class="fa fa-backward"></i>',
27660             disabled: true,
27661             preventDefault: true,
27662             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27663         });
27664     //this.addSeparator();
27665         
27666         
27667         var field = this.navgroup.addItem( {
27668             tagtype : 'span',
27669             cls : 'x-paging-position  btn-outline-secondary',
27670              disabled: true,
27671             html : this.beforePageText  +
27672                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27673                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27674          } ); //?? escaped?
27675         
27676         this.field = field.el.select('input', true).first();
27677         this.field.on("keydown", this.onPagingKeydown, this);
27678         this.field.on("focus", function(){this.dom.select();});
27679     
27680     
27681         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27682         //this.field.setHeight(18);
27683         //this.addSeparator();
27684         this.next = this.navgroup.addItem({
27685             tooltip: this.nextText,
27686             cls: "next btn-outline-secondary",
27687             html : ' <i class="fa fa-forward"></i>',
27688             disabled: true,
27689             preventDefault: true,
27690             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27691         });
27692         this.last = this.navgroup.addItem({
27693             tooltip: this.lastText,
27694             html : ' <i class="fa fa-step-forward"></i>',
27695             cls: "next btn-outline-secondary",
27696             disabled: true,
27697             preventDefault: true,
27698             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27699         });
27700     //this.addSeparator();
27701         this.loading = this.navgroup.addItem({
27702             tooltip: this.refreshText,
27703             cls: "btn-outline-secondary",
27704             html : ' <i class="fa fa-refresh"></i>',
27705             preventDefault: true,
27706             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27707         });
27708         
27709     },
27710
27711     // private
27712     updateInfo : function(){
27713         if(this.displayEl){
27714             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27715             var msg = count == 0 ?
27716                 this.emptyMsg :
27717                 String.format(
27718                     this.displayMsg,
27719                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27720                 );
27721             this.displayEl.update(msg);
27722         }
27723     },
27724
27725     // private
27726     onLoad : function(ds, r, o)
27727     {
27728         this.cursor = o.params && o.params.start ? o.params.start : 0;
27729         
27730         var d = this.getPageData(),
27731             ap = d.activePage,
27732             ps = d.pages;
27733         
27734         
27735         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27736         this.field.dom.value = ap;
27737         this.first.setDisabled(ap == 1);
27738         this.prev.setDisabled(ap == 1);
27739         this.next.setDisabled(ap == ps);
27740         this.last.setDisabled(ap == ps);
27741         this.loading.enable();
27742         this.updateInfo();
27743     },
27744
27745     // private
27746     getPageData : function(){
27747         var total = this.ds.getTotalCount();
27748         return {
27749             total : total,
27750             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27751             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27752         };
27753     },
27754
27755     // private
27756     onLoadError : function(){
27757         this.loading.enable();
27758     },
27759
27760     // private
27761     onPagingKeydown : function(e){
27762         var k = e.getKey();
27763         var d = this.getPageData();
27764         if(k == e.RETURN){
27765             var v = this.field.dom.value, pageNum;
27766             if(!v || isNaN(pageNum = parseInt(v, 10))){
27767                 this.field.dom.value = d.activePage;
27768                 return;
27769             }
27770             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27771             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27772             e.stopEvent();
27773         }
27774         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))
27775         {
27776           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27777           this.field.dom.value = pageNum;
27778           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27779           e.stopEvent();
27780         }
27781         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27782         {
27783           var v = this.field.dom.value, pageNum; 
27784           var increment = (e.shiftKey) ? 10 : 1;
27785           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27786                 increment *= -1;
27787           }
27788           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27789             this.field.dom.value = d.activePage;
27790             return;
27791           }
27792           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27793           {
27794             this.field.dom.value = parseInt(v, 10) + increment;
27795             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27796             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27797           }
27798           e.stopEvent();
27799         }
27800     },
27801
27802     // private
27803     beforeLoad : function(){
27804         if(this.loading){
27805             this.loading.disable();
27806         }
27807     },
27808
27809     // private
27810     onClick : function(which){
27811         
27812         var ds = this.ds;
27813         if (!ds) {
27814             return;
27815         }
27816         
27817         switch(which){
27818             case "first":
27819                 ds.load({params:{start: 0, limit: this.pageSize}});
27820             break;
27821             case "prev":
27822                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27823             break;
27824             case "next":
27825                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27826             break;
27827             case "last":
27828                 var total = ds.getTotalCount();
27829                 var extra = total % this.pageSize;
27830                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27831                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27832             break;
27833             case "refresh":
27834                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27835             break;
27836         }
27837     },
27838
27839     /**
27840      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27841      * @param {Roo.data.Store} store The data store to unbind
27842      */
27843     unbind : function(ds){
27844         ds.un("beforeload", this.beforeLoad, this);
27845         ds.un("load", this.onLoad, this);
27846         ds.un("loadexception", this.onLoadError, this);
27847         ds.un("remove", this.updateInfo, this);
27848         ds.un("add", this.updateInfo, this);
27849         this.ds = undefined;
27850     },
27851
27852     /**
27853      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27854      * @param {Roo.data.Store} store The data store to bind
27855      */
27856     bind : function(ds){
27857         ds.on("beforeload", this.beforeLoad, this);
27858         ds.on("load", this.onLoad, this);
27859         ds.on("loadexception", this.onLoadError, this);
27860         ds.on("remove", this.updateInfo, this);
27861         ds.on("add", this.updateInfo, this);
27862         this.ds = ds;
27863     }
27864 });/*
27865  * - LGPL
27866  *
27867  * element
27868  * 
27869  */
27870
27871 /**
27872  * @class Roo.bootstrap.MessageBar
27873  * @extends Roo.bootstrap.Component
27874  * Bootstrap MessageBar class
27875  * @cfg {String} html contents of the MessageBar
27876  * @cfg {String} weight (info | success | warning | danger) default info
27877  * @cfg {String} beforeClass insert the bar before the given class
27878  * @cfg {Boolean} closable (true | false) default false
27879  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27880  * 
27881  * @constructor
27882  * Create a new Element
27883  * @param {Object} config The config object
27884  */
27885
27886 Roo.bootstrap.MessageBar = function(config){
27887     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27888 };
27889
27890 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27891     
27892     html: '',
27893     weight: 'info',
27894     closable: false,
27895     fixed: false,
27896     beforeClass: 'bootstrap-sticky-wrap',
27897     
27898     getAutoCreate : function(){
27899         
27900         var cfg = {
27901             tag: 'div',
27902             cls: 'alert alert-dismissable alert-' + this.weight,
27903             cn: [
27904                 {
27905                     tag: 'span',
27906                     cls: 'message',
27907                     html: this.html || ''
27908                 }
27909             ]
27910         };
27911         
27912         if(this.fixed){
27913             cfg.cls += ' alert-messages-fixed';
27914         }
27915         
27916         if(this.closable){
27917             cfg.cn.push({
27918                 tag: 'button',
27919                 cls: 'close',
27920                 html: 'x'
27921             });
27922         }
27923         
27924         return cfg;
27925     },
27926     
27927     onRender : function(ct, position)
27928     {
27929         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27930         
27931         if(!this.el){
27932             var cfg = Roo.apply({},  this.getAutoCreate());
27933             cfg.id = Roo.id();
27934             
27935             if (this.cls) {
27936                 cfg.cls += ' ' + this.cls;
27937             }
27938             if (this.style) {
27939                 cfg.style = this.style;
27940             }
27941             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27942             
27943             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27944         }
27945         
27946         this.el.select('>button.close').on('click', this.hide, this);
27947         
27948     },
27949     
27950     show : function()
27951     {
27952         if (!this.rendered) {
27953             this.render();
27954         }
27955         
27956         this.el.show();
27957         
27958         this.fireEvent('show', this);
27959         
27960     },
27961     
27962     hide : function()
27963     {
27964         if (!this.rendered) {
27965             this.render();
27966         }
27967         
27968         this.el.hide();
27969         
27970         this.fireEvent('hide', this);
27971     },
27972     
27973     update : function()
27974     {
27975 //        var e = this.el.dom.firstChild;
27976 //        
27977 //        if(this.closable){
27978 //            e = e.nextSibling;
27979 //        }
27980 //        
27981 //        e.data = this.html || '';
27982
27983         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27984     }
27985    
27986 });
27987
27988  
27989
27990      /*
27991  * - LGPL
27992  *
27993  * Graph
27994  * 
27995  */
27996
27997
27998 /**
27999  * @class Roo.bootstrap.Graph
28000  * @extends Roo.bootstrap.Component
28001  * Bootstrap Graph class
28002 > Prameters
28003  -sm {number} sm 4
28004  -md {number} md 5
28005  @cfg {String} graphtype  bar | vbar | pie
28006  @cfg {number} g_x coodinator | centre x (pie)
28007  @cfg {number} g_y coodinator | centre y (pie)
28008  @cfg {number} g_r radius (pie)
28009  @cfg {number} g_height height of the chart (respected by all elements in the set)
28010  @cfg {number} g_width width of the chart (respected by all elements in the set)
28011  @cfg {Object} title The title of the chart
28012     
28013  -{Array}  values
28014  -opts (object) options for the chart 
28015      o {
28016      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28017      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28018      o vgutter (number)
28019      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.
28020      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28021      o to
28022      o stretch (boolean)
28023      o }
28024  -opts (object) options for the pie
28025      o{
28026      o cut
28027      o startAngle (number)
28028      o endAngle (number)
28029      } 
28030  *
28031  * @constructor
28032  * Create a new Input
28033  * @param {Object} config The config object
28034  */
28035
28036 Roo.bootstrap.Graph = function(config){
28037     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28038     
28039     this.addEvents({
28040         // img events
28041         /**
28042          * @event click
28043          * The img click event for the img.
28044          * @param {Roo.EventObject} e
28045          */
28046         "click" : true
28047     });
28048 };
28049
28050 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28051     
28052     sm: 4,
28053     md: 5,
28054     graphtype: 'bar',
28055     g_height: 250,
28056     g_width: 400,
28057     g_x: 50,
28058     g_y: 50,
28059     g_r: 30,
28060     opts:{
28061         //g_colors: this.colors,
28062         g_type: 'soft',
28063         g_gutter: '20%'
28064
28065     },
28066     title : false,
28067
28068     getAutoCreate : function(){
28069         
28070         var cfg = {
28071             tag: 'div',
28072             html : null
28073         };
28074         
28075         
28076         return  cfg;
28077     },
28078
28079     onRender : function(ct,position){
28080         
28081         
28082         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28083         
28084         if (typeof(Raphael) == 'undefined') {
28085             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28086             return;
28087         }
28088         
28089         this.raphael = Raphael(this.el.dom);
28090         
28091                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28092                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28093                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28094                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28095                 /*
28096                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28097                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28098                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28099                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28100                 
28101                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28102                 r.barchart(330, 10, 300, 220, data1);
28103                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28104                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28105                 */
28106                 
28107                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28108                 // r.barchart(30, 30, 560, 250,  xdata, {
28109                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28110                 //     axis : "0 0 1 1",
28111                 //     axisxlabels :  xdata
28112                 //     //yvalues : cols,
28113                    
28114                 // });
28115 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28116 //        
28117 //        this.load(null,xdata,{
28118 //                axis : "0 0 1 1",
28119 //                axisxlabels :  xdata
28120 //                });
28121
28122     },
28123
28124     load : function(graphtype,xdata,opts)
28125     {
28126         this.raphael.clear();
28127         if(!graphtype) {
28128             graphtype = this.graphtype;
28129         }
28130         if(!opts){
28131             opts = this.opts;
28132         }
28133         var r = this.raphael,
28134             fin = function () {
28135                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28136             },
28137             fout = function () {
28138                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28139             },
28140             pfin = function() {
28141                 this.sector.stop();
28142                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28143
28144                 if (this.label) {
28145                     this.label[0].stop();
28146                     this.label[0].attr({ r: 7.5 });
28147                     this.label[1].attr({ "font-weight": 800 });
28148                 }
28149             },
28150             pfout = function() {
28151                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28152
28153                 if (this.label) {
28154                     this.label[0].animate({ r: 5 }, 500, "bounce");
28155                     this.label[1].attr({ "font-weight": 400 });
28156                 }
28157             };
28158
28159         switch(graphtype){
28160             case 'bar':
28161                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28162                 break;
28163             case 'hbar':
28164                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28165                 break;
28166             case 'pie':
28167 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28168 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28169 //            
28170                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28171                 
28172                 break;
28173
28174         }
28175         
28176         if(this.title){
28177             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28178         }
28179         
28180     },
28181     
28182     setTitle: function(o)
28183     {
28184         this.title = o;
28185     },
28186     
28187     initEvents: function() {
28188         
28189         if(!this.href){
28190             this.el.on('click', this.onClick, this);
28191         }
28192     },
28193     
28194     onClick : function(e)
28195     {
28196         Roo.log('img onclick');
28197         this.fireEvent('click', this, e);
28198     }
28199    
28200 });
28201
28202  
28203 /*
28204  * - LGPL
28205  *
28206  * numberBox
28207  * 
28208  */
28209 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28210
28211 /**
28212  * @class Roo.bootstrap.dash.NumberBox
28213  * @extends Roo.bootstrap.Component
28214  * Bootstrap NumberBox class
28215  * @cfg {String} headline Box headline
28216  * @cfg {String} content Box content
28217  * @cfg {String} icon Box icon
28218  * @cfg {String} footer Footer text
28219  * @cfg {String} fhref Footer href
28220  * 
28221  * @constructor
28222  * Create a new NumberBox
28223  * @param {Object} config The config object
28224  */
28225
28226
28227 Roo.bootstrap.dash.NumberBox = function(config){
28228     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28229     
28230 };
28231
28232 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28233     
28234     headline : '',
28235     content : '',
28236     icon : '',
28237     footer : '',
28238     fhref : '',
28239     ficon : '',
28240     
28241     getAutoCreate : function(){
28242         
28243         var cfg = {
28244             tag : 'div',
28245             cls : 'small-box ',
28246             cn : [
28247                 {
28248                     tag : 'div',
28249                     cls : 'inner',
28250                     cn :[
28251                         {
28252                             tag : 'h3',
28253                             cls : 'roo-headline',
28254                             html : this.headline
28255                         },
28256                         {
28257                             tag : 'p',
28258                             cls : 'roo-content',
28259                             html : this.content
28260                         }
28261                     ]
28262                 }
28263             ]
28264         };
28265         
28266         if(this.icon){
28267             cfg.cn.push({
28268                 tag : 'div',
28269                 cls : 'icon',
28270                 cn :[
28271                     {
28272                         tag : 'i',
28273                         cls : 'ion ' + this.icon
28274                     }
28275                 ]
28276             });
28277         }
28278         
28279         if(this.footer){
28280             var footer = {
28281                 tag : 'a',
28282                 cls : 'small-box-footer',
28283                 href : this.fhref || '#',
28284                 html : this.footer
28285             };
28286             
28287             cfg.cn.push(footer);
28288             
28289         }
28290         
28291         return  cfg;
28292     },
28293
28294     onRender : function(ct,position){
28295         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28296
28297
28298        
28299                 
28300     },
28301
28302     setHeadline: function (value)
28303     {
28304         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28305     },
28306     
28307     setFooter: function (value, href)
28308     {
28309         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28310         
28311         if(href){
28312             this.el.select('a.small-box-footer',true).first().attr('href', href);
28313         }
28314         
28315     },
28316
28317     setContent: function (value)
28318     {
28319         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28320     },
28321
28322     initEvents: function() 
28323     {   
28324         
28325     }
28326     
28327 });
28328
28329  
28330 /*
28331  * - LGPL
28332  *
28333  * TabBox
28334  * 
28335  */
28336 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28337
28338 /**
28339  * @class Roo.bootstrap.dash.TabBox
28340  * @extends Roo.bootstrap.Component
28341  * Bootstrap TabBox class
28342  * @cfg {String} title Title of the TabBox
28343  * @cfg {String} icon Icon of the TabBox
28344  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28345  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28346  * 
28347  * @constructor
28348  * Create a new TabBox
28349  * @param {Object} config The config object
28350  */
28351
28352
28353 Roo.bootstrap.dash.TabBox = function(config){
28354     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28355     this.addEvents({
28356         // raw events
28357         /**
28358          * @event addpane
28359          * When a pane is added
28360          * @param {Roo.bootstrap.dash.TabPane} pane
28361          */
28362         "addpane" : true,
28363         /**
28364          * @event activatepane
28365          * When a pane is activated
28366          * @param {Roo.bootstrap.dash.TabPane} pane
28367          */
28368         "activatepane" : true
28369         
28370          
28371     });
28372     
28373     this.panes = [];
28374 };
28375
28376 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28377
28378     title : '',
28379     icon : false,
28380     showtabs : true,
28381     tabScrollable : false,
28382     
28383     getChildContainer : function()
28384     {
28385         return this.el.select('.tab-content', true).first();
28386     },
28387     
28388     getAutoCreate : function(){
28389         
28390         var header = {
28391             tag: 'li',
28392             cls: 'pull-left header',
28393             html: this.title,
28394             cn : []
28395         };
28396         
28397         if(this.icon){
28398             header.cn.push({
28399                 tag: 'i',
28400                 cls: 'fa ' + this.icon
28401             });
28402         }
28403         
28404         var h = {
28405             tag: 'ul',
28406             cls: 'nav nav-tabs pull-right',
28407             cn: [
28408                 header
28409             ]
28410         };
28411         
28412         if(this.tabScrollable){
28413             h = {
28414                 tag: 'div',
28415                 cls: 'tab-header',
28416                 cn: [
28417                     {
28418                         tag: 'ul',
28419                         cls: 'nav nav-tabs pull-right',
28420                         cn: [
28421                             header
28422                         ]
28423                     }
28424                 ]
28425             };
28426         }
28427         
28428         var cfg = {
28429             tag: 'div',
28430             cls: 'nav-tabs-custom',
28431             cn: [
28432                 h,
28433                 {
28434                     tag: 'div',
28435                     cls: 'tab-content no-padding',
28436                     cn: []
28437                 }
28438             ]
28439         };
28440
28441         return  cfg;
28442     },
28443     initEvents : function()
28444     {
28445         //Roo.log('add add pane handler');
28446         this.on('addpane', this.onAddPane, this);
28447     },
28448      /**
28449      * Updates the box title
28450      * @param {String} html to set the title to.
28451      */
28452     setTitle : function(value)
28453     {
28454         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28455     },
28456     onAddPane : function(pane)
28457     {
28458         this.panes.push(pane);
28459         //Roo.log('addpane');
28460         //Roo.log(pane);
28461         // tabs are rendere left to right..
28462         if(!this.showtabs){
28463             return;
28464         }
28465         
28466         var ctr = this.el.select('.nav-tabs', true).first();
28467          
28468          
28469         var existing = ctr.select('.nav-tab',true);
28470         var qty = existing.getCount();;
28471         
28472         
28473         var tab = ctr.createChild({
28474             tag : 'li',
28475             cls : 'nav-tab' + (qty ? '' : ' active'),
28476             cn : [
28477                 {
28478                     tag : 'a',
28479                     href:'#',
28480                     html : pane.title
28481                 }
28482             ]
28483         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28484         pane.tab = tab;
28485         
28486         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28487         if (!qty) {
28488             pane.el.addClass('active');
28489         }
28490         
28491                 
28492     },
28493     onTabClick : function(ev,un,ob,pane)
28494     {
28495         //Roo.log('tab - prev default');
28496         ev.preventDefault();
28497         
28498         
28499         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28500         pane.tab.addClass('active');
28501         //Roo.log(pane.title);
28502         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28503         // technically we should have a deactivate event.. but maybe add later.
28504         // and it should not de-activate the selected tab...
28505         this.fireEvent('activatepane', pane);
28506         pane.el.addClass('active');
28507         pane.fireEvent('activate');
28508         
28509         
28510     },
28511     
28512     getActivePane : function()
28513     {
28514         var r = false;
28515         Roo.each(this.panes, function(p) {
28516             if(p.el.hasClass('active')){
28517                 r = p;
28518                 return false;
28519             }
28520             
28521             return;
28522         });
28523         
28524         return r;
28525     }
28526     
28527     
28528 });
28529
28530  
28531 /*
28532  * - LGPL
28533  *
28534  * Tab pane
28535  * 
28536  */
28537 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28538 /**
28539  * @class Roo.bootstrap.TabPane
28540  * @extends Roo.bootstrap.Component
28541  * Bootstrap TabPane class
28542  * @cfg {Boolean} active (false | true) Default false
28543  * @cfg {String} title title of panel
28544
28545  * 
28546  * @constructor
28547  * Create a new TabPane
28548  * @param {Object} config The config object
28549  */
28550
28551 Roo.bootstrap.dash.TabPane = function(config){
28552     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28553     
28554     this.addEvents({
28555         // raw events
28556         /**
28557          * @event activate
28558          * When a pane is activated
28559          * @param {Roo.bootstrap.dash.TabPane} pane
28560          */
28561         "activate" : true
28562          
28563     });
28564 };
28565
28566 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28567     
28568     active : false,
28569     title : '',
28570     
28571     // the tabBox that this is attached to.
28572     tab : false,
28573      
28574     getAutoCreate : function() 
28575     {
28576         var cfg = {
28577             tag: 'div',
28578             cls: 'tab-pane'
28579         };
28580         
28581         if(this.active){
28582             cfg.cls += ' active';
28583         }
28584         
28585         return cfg;
28586     },
28587     initEvents  : function()
28588     {
28589         //Roo.log('trigger add pane handler');
28590         this.parent().fireEvent('addpane', this)
28591     },
28592     
28593      /**
28594      * Updates the tab title 
28595      * @param {String} html to set the title to.
28596      */
28597     setTitle: function(str)
28598     {
28599         if (!this.tab) {
28600             return;
28601         }
28602         this.title = str;
28603         this.tab.select('a', true).first().dom.innerHTML = str;
28604         
28605     }
28606     
28607     
28608     
28609 });
28610
28611  
28612
28613
28614  /*
28615  * - LGPL
28616  *
28617  * menu
28618  * 
28619  */
28620 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28621
28622 /**
28623  * @class Roo.bootstrap.menu.Menu
28624  * @extends Roo.bootstrap.Component
28625  * Bootstrap Menu class - container for Menu
28626  * @cfg {String} html Text of the menu
28627  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28628  * @cfg {String} icon Font awesome icon
28629  * @cfg {String} pos Menu align to (top | bottom) default bottom
28630  * 
28631  * 
28632  * @constructor
28633  * Create a new Menu
28634  * @param {Object} config The config object
28635  */
28636
28637
28638 Roo.bootstrap.menu.Menu = function(config){
28639     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28640     
28641     this.addEvents({
28642         /**
28643          * @event beforeshow
28644          * Fires before this menu is displayed
28645          * @param {Roo.bootstrap.menu.Menu} this
28646          */
28647         beforeshow : true,
28648         /**
28649          * @event beforehide
28650          * Fires before this menu is hidden
28651          * @param {Roo.bootstrap.menu.Menu} this
28652          */
28653         beforehide : true,
28654         /**
28655          * @event show
28656          * Fires after this menu is displayed
28657          * @param {Roo.bootstrap.menu.Menu} this
28658          */
28659         show : true,
28660         /**
28661          * @event hide
28662          * Fires after this menu is hidden
28663          * @param {Roo.bootstrap.menu.Menu} this
28664          */
28665         hide : true,
28666         /**
28667          * @event click
28668          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28669          * @param {Roo.bootstrap.menu.Menu} this
28670          * @param {Roo.EventObject} e
28671          */
28672         click : true
28673     });
28674     
28675 };
28676
28677 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28678     
28679     submenu : false,
28680     html : '',
28681     weight : 'default',
28682     icon : false,
28683     pos : 'bottom',
28684     
28685     
28686     getChildContainer : function() {
28687         if(this.isSubMenu){
28688             return this.el;
28689         }
28690         
28691         return this.el.select('ul.dropdown-menu', true).first();  
28692     },
28693     
28694     getAutoCreate : function()
28695     {
28696         var text = [
28697             {
28698                 tag : 'span',
28699                 cls : 'roo-menu-text',
28700                 html : this.html
28701             }
28702         ];
28703         
28704         if(this.icon){
28705             text.unshift({
28706                 tag : 'i',
28707                 cls : 'fa ' + this.icon
28708             })
28709         }
28710         
28711         
28712         var cfg = {
28713             tag : 'div',
28714             cls : 'btn-group',
28715             cn : [
28716                 {
28717                     tag : 'button',
28718                     cls : 'dropdown-button btn btn-' + this.weight,
28719                     cn : text
28720                 },
28721                 {
28722                     tag : 'button',
28723                     cls : 'dropdown-toggle btn btn-' + this.weight,
28724                     cn : [
28725                         {
28726                             tag : 'span',
28727                             cls : 'caret'
28728                         }
28729                     ]
28730                 },
28731                 {
28732                     tag : 'ul',
28733                     cls : 'dropdown-menu'
28734                 }
28735             ]
28736             
28737         };
28738         
28739         if(this.pos == 'top'){
28740             cfg.cls += ' dropup';
28741         }
28742         
28743         if(this.isSubMenu){
28744             cfg = {
28745                 tag : 'ul',
28746                 cls : 'dropdown-menu'
28747             }
28748         }
28749         
28750         return cfg;
28751     },
28752     
28753     onRender : function(ct, position)
28754     {
28755         this.isSubMenu = ct.hasClass('dropdown-submenu');
28756         
28757         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28758     },
28759     
28760     initEvents : function() 
28761     {
28762         if(this.isSubMenu){
28763             return;
28764         }
28765         
28766         this.hidden = true;
28767         
28768         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28769         this.triggerEl.on('click', this.onTriggerPress, this);
28770         
28771         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28772         this.buttonEl.on('click', this.onClick, this);
28773         
28774     },
28775     
28776     list : function()
28777     {
28778         if(this.isSubMenu){
28779             return this.el;
28780         }
28781         
28782         return this.el.select('ul.dropdown-menu', true).first();
28783     },
28784     
28785     onClick : function(e)
28786     {
28787         this.fireEvent("click", this, e);
28788     },
28789     
28790     onTriggerPress  : function(e)
28791     {   
28792         if (this.isVisible()) {
28793             this.hide();
28794         } else {
28795             this.show();
28796         }
28797     },
28798     
28799     isVisible : function(){
28800         return !this.hidden;
28801     },
28802     
28803     show : function()
28804     {
28805         this.fireEvent("beforeshow", this);
28806         
28807         this.hidden = false;
28808         this.el.addClass('open');
28809         
28810         Roo.get(document).on("mouseup", this.onMouseUp, this);
28811         
28812         this.fireEvent("show", this);
28813         
28814         
28815     },
28816     
28817     hide : function()
28818     {
28819         this.fireEvent("beforehide", this);
28820         
28821         this.hidden = true;
28822         this.el.removeClass('open');
28823         
28824         Roo.get(document).un("mouseup", this.onMouseUp);
28825         
28826         this.fireEvent("hide", this);
28827     },
28828     
28829     onMouseUp : function()
28830     {
28831         this.hide();
28832     }
28833     
28834 });
28835
28836  
28837  /*
28838  * - LGPL
28839  *
28840  * menu item
28841  * 
28842  */
28843 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28844
28845 /**
28846  * @class Roo.bootstrap.menu.Item
28847  * @extends Roo.bootstrap.Component
28848  * Bootstrap MenuItem class
28849  * @cfg {Boolean} submenu (true | false) default false
28850  * @cfg {String} html text of the item
28851  * @cfg {String} href the link
28852  * @cfg {Boolean} disable (true | false) default false
28853  * @cfg {Boolean} preventDefault (true | false) default true
28854  * @cfg {String} icon Font awesome icon
28855  * @cfg {String} pos Submenu align to (left | right) default right 
28856  * 
28857  * 
28858  * @constructor
28859  * Create a new Item
28860  * @param {Object} config The config object
28861  */
28862
28863
28864 Roo.bootstrap.menu.Item = function(config){
28865     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28866     this.addEvents({
28867         /**
28868          * @event mouseover
28869          * Fires when the mouse is hovering over this menu
28870          * @param {Roo.bootstrap.menu.Item} this
28871          * @param {Roo.EventObject} e
28872          */
28873         mouseover : true,
28874         /**
28875          * @event mouseout
28876          * Fires when the mouse exits this menu
28877          * @param {Roo.bootstrap.menu.Item} this
28878          * @param {Roo.EventObject} e
28879          */
28880         mouseout : true,
28881         // raw events
28882         /**
28883          * @event click
28884          * The raw click event for the entire grid.
28885          * @param {Roo.EventObject} e
28886          */
28887         click : true
28888     });
28889 };
28890
28891 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28892     
28893     submenu : false,
28894     href : '',
28895     html : '',
28896     preventDefault: true,
28897     disable : false,
28898     icon : false,
28899     pos : 'right',
28900     
28901     getAutoCreate : function()
28902     {
28903         var text = [
28904             {
28905                 tag : 'span',
28906                 cls : 'roo-menu-item-text',
28907                 html : this.html
28908             }
28909         ];
28910         
28911         if(this.icon){
28912             text.unshift({
28913                 tag : 'i',
28914                 cls : 'fa ' + this.icon
28915             })
28916         }
28917         
28918         var cfg = {
28919             tag : 'li',
28920             cn : [
28921                 {
28922                     tag : 'a',
28923                     href : this.href || '#',
28924                     cn : text
28925                 }
28926             ]
28927         };
28928         
28929         if(this.disable){
28930             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28931         }
28932         
28933         if(this.submenu){
28934             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28935             
28936             if(this.pos == 'left'){
28937                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28938             }
28939         }
28940         
28941         return cfg;
28942     },
28943     
28944     initEvents : function() 
28945     {
28946         this.el.on('mouseover', this.onMouseOver, this);
28947         this.el.on('mouseout', this.onMouseOut, this);
28948         
28949         this.el.select('a', true).first().on('click', this.onClick, this);
28950         
28951     },
28952     
28953     onClick : function(e)
28954     {
28955         if(this.preventDefault){
28956             e.preventDefault();
28957         }
28958         
28959         this.fireEvent("click", this, e);
28960     },
28961     
28962     onMouseOver : function(e)
28963     {
28964         if(this.submenu && this.pos == 'left'){
28965             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28966         }
28967         
28968         this.fireEvent("mouseover", this, e);
28969     },
28970     
28971     onMouseOut : function(e)
28972     {
28973         this.fireEvent("mouseout", this, e);
28974     }
28975 });
28976
28977  
28978
28979  /*
28980  * - LGPL
28981  *
28982  * menu separator
28983  * 
28984  */
28985 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28986
28987 /**
28988  * @class Roo.bootstrap.menu.Separator
28989  * @extends Roo.bootstrap.Component
28990  * Bootstrap Separator class
28991  * 
28992  * @constructor
28993  * Create a new Separator
28994  * @param {Object} config The config object
28995  */
28996
28997
28998 Roo.bootstrap.menu.Separator = function(config){
28999     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29000 };
29001
29002 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29003     
29004     getAutoCreate : function(){
29005         var cfg = {
29006             tag : 'li',
29007             cls: 'dropdown-divider divider'
29008         };
29009         
29010         return cfg;
29011     }
29012    
29013 });
29014
29015  
29016
29017  /*
29018  * - LGPL
29019  *
29020  * Tooltip
29021  * 
29022  */
29023
29024 /**
29025  * @class Roo.bootstrap.Tooltip
29026  * Bootstrap Tooltip class
29027  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29028  * to determine which dom element triggers the tooltip.
29029  * 
29030  * It needs to add support for additional attributes like tooltip-position
29031  * 
29032  * @constructor
29033  * Create a new Toolti
29034  * @param {Object} config The config object
29035  */
29036
29037 Roo.bootstrap.Tooltip = function(config){
29038     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29039     
29040     this.alignment = Roo.bootstrap.Tooltip.alignment;
29041     
29042     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29043         this.alignment = config.alignment;
29044     }
29045     
29046 };
29047
29048 Roo.apply(Roo.bootstrap.Tooltip, {
29049     /**
29050      * @function init initialize tooltip monitoring.
29051      * @static
29052      */
29053     currentEl : false,
29054     currentTip : false,
29055     currentRegion : false,
29056     
29057     //  init : delay?
29058     
29059     init : function()
29060     {
29061         Roo.get(document).on('mouseover', this.enter ,this);
29062         Roo.get(document).on('mouseout', this.leave, this);
29063          
29064         
29065         this.currentTip = new Roo.bootstrap.Tooltip();
29066     },
29067     
29068     enter : function(ev)
29069     {
29070         var dom = ev.getTarget();
29071         
29072         //Roo.log(['enter',dom]);
29073         var el = Roo.fly(dom);
29074         if (this.currentEl) {
29075             //Roo.log(dom);
29076             //Roo.log(this.currentEl);
29077             //Roo.log(this.currentEl.contains(dom));
29078             if (this.currentEl == el) {
29079                 return;
29080             }
29081             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29082                 return;
29083             }
29084
29085         }
29086         
29087         if (this.currentTip.el) {
29088             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29089         }    
29090         //Roo.log(ev);
29091         
29092         if(!el || el.dom == document){
29093             return;
29094         }
29095         
29096         var bindEl = el; 
29097         var pel = false;
29098         if (!el.attr('tooltip')) {
29099             pel = el.findParent("[tooltip]");
29100             if (pel) {
29101                 bindEl = Roo.get(pel);
29102             }
29103         }
29104         
29105        
29106         
29107         // you can not look for children, as if el is the body.. then everythign is the child..
29108         if (!pel && !el.attr('tooltip')) { //
29109             if (!el.select("[tooltip]").elements.length) {
29110                 return;
29111             }
29112             // is the mouse over this child...?
29113             bindEl = el.select("[tooltip]").first();
29114             var xy = ev.getXY();
29115             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29116                 //Roo.log("not in region.");
29117                 return;
29118             }
29119             //Roo.log("child element over..");
29120             
29121         }
29122         this.currentEl = el;
29123         this.currentTip.bind(bindEl);
29124         this.currentRegion = Roo.lib.Region.getRegion(dom);
29125         this.currentTip.enter();
29126         
29127     },
29128     leave : function(ev)
29129     {
29130         var dom = ev.getTarget();
29131         //Roo.log(['leave',dom]);
29132         if (!this.currentEl) {
29133             return;
29134         }
29135         
29136         
29137         if (dom != this.currentEl.dom) {
29138             return;
29139         }
29140         var xy = ev.getXY();
29141         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29142             return;
29143         }
29144         // only activate leave if mouse cursor is outside... bounding box..
29145         
29146         
29147         
29148         
29149         if (this.currentTip) {
29150             this.currentTip.leave();
29151         }
29152         //Roo.log('clear currentEl');
29153         this.currentEl = false;
29154         
29155         
29156     },
29157     alignment : {
29158         'left' : ['r-l', [-2,0], 'right'],
29159         'right' : ['l-r', [2,0], 'left'],
29160         'bottom' : ['t-b', [0,2], 'top'],
29161         'top' : [ 'b-t', [0,-2], 'bottom']
29162     }
29163     
29164 });
29165
29166
29167 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29168     
29169     
29170     bindEl : false,
29171     
29172     delay : null, // can be { show : 300 , hide: 500}
29173     
29174     timeout : null,
29175     
29176     hoverState : null, //???
29177     
29178     placement : 'bottom', 
29179     
29180     alignment : false,
29181     
29182     getAutoCreate : function(){
29183     
29184         var cfg = {
29185            cls : 'tooltip',   
29186            role : 'tooltip',
29187            cn : [
29188                 {
29189                     cls : 'tooltip-arrow arrow'
29190                 },
29191                 {
29192                     cls : 'tooltip-inner'
29193                 }
29194            ]
29195         };
29196         
29197         return cfg;
29198     },
29199     bind : function(el)
29200     {
29201         this.bindEl = el;
29202     },
29203     
29204     initEvents : function()
29205     {
29206         this.arrowEl = this.el.select('.arrow', true).first();
29207         this.innerEl = this.el.select('.tooltip-inner', true).first();
29208     },
29209     
29210     enter : function () {
29211        
29212         if (this.timeout != null) {
29213             clearTimeout(this.timeout);
29214         }
29215         
29216         this.hoverState = 'in';
29217          //Roo.log("enter - show");
29218         if (!this.delay || !this.delay.show) {
29219             this.show();
29220             return;
29221         }
29222         var _t = this;
29223         this.timeout = setTimeout(function () {
29224             if (_t.hoverState == 'in') {
29225                 _t.show();
29226             }
29227         }, this.delay.show);
29228     },
29229     leave : function()
29230     {
29231         clearTimeout(this.timeout);
29232     
29233         this.hoverState = 'out';
29234          if (!this.delay || !this.delay.hide) {
29235             this.hide();
29236             return;
29237         }
29238        
29239         var _t = this;
29240         this.timeout = setTimeout(function () {
29241             //Roo.log("leave - timeout");
29242             
29243             if (_t.hoverState == 'out') {
29244                 _t.hide();
29245                 Roo.bootstrap.Tooltip.currentEl = false;
29246             }
29247         }, delay);
29248     },
29249     
29250     show : function (msg)
29251     {
29252         if (!this.el) {
29253             this.render(document.body);
29254         }
29255         // set content.
29256         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29257         
29258         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29259         
29260         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29261         
29262         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29263                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29264         
29265         var placement = typeof this.placement == 'function' ?
29266             this.placement.call(this, this.el, on_el) :
29267             this.placement;
29268             
29269         var autoToken = /\s?auto?\s?/i;
29270         var autoPlace = autoToken.test(placement);
29271         if (autoPlace) {
29272             placement = placement.replace(autoToken, '') || 'top';
29273         }
29274         
29275         //this.el.detach()
29276         //this.el.setXY([0,0]);
29277         this.el.show();
29278         //this.el.dom.style.display='block';
29279         
29280         //this.el.appendTo(on_el);
29281         
29282         var p = this.getPosition();
29283         var box = this.el.getBox();
29284         
29285         if (autoPlace) {
29286             // fixme..
29287         }
29288         
29289         var align = this.alignment[placement];
29290         
29291         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29292         
29293         if(placement == 'top' || placement == 'bottom'){
29294             if(xy[0] < 0){
29295                 placement = 'right';
29296             }
29297             
29298             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29299                 placement = 'left';
29300             }
29301             
29302             var scroll = Roo.select('body', true).first().getScroll();
29303             
29304             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29305                 placement = 'top';
29306             }
29307             
29308             align = this.alignment[placement];
29309             
29310             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29311             
29312         }
29313         
29314         var elems = document.getElementsByTagName('div');
29315         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29316         for (var i = 0; i < elems.length; i++) {
29317           var zindex = Number.parseInt(
29318                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29319                 10
29320           );
29321           if (zindex > highest) {
29322             highest = zindex;
29323           }
29324         }
29325         
29326         
29327         
29328         this.el.dom.style.zIndex = highest;
29329         
29330         this.el.alignTo(this.bindEl, align[0],align[1]);
29331         //var arrow = this.el.select('.arrow',true).first();
29332         //arrow.set(align[2], 
29333         
29334         this.el.addClass(placement);
29335         this.el.addClass("bs-tooltip-"+ placement);
29336         
29337         this.el.addClass('in fade show');
29338         
29339         this.hoverState = null;
29340         
29341         if (this.el.hasClass('fade')) {
29342             // fade it?
29343         }
29344         
29345         
29346         
29347         
29348         
29349     },
29350     hide : function()
29351     {
29352          
29353         if (!this.el) {
29354             return;
29355         }
29356         //this.el.setXY([0,0]);
29357         this.el.removeClass(['show', 'in']);
29358         //this.el.hide();
29359         
29360     }
29361     
29362 });
29363  
29364
29365  /*
29366  * - LGPL
29367  *
29368  * Location Picker
29369  * 
29370  */
29371
29372 /**
29373  * @class Roo.bootstrap.LocationPicker
29374  * @extends Roo.bootstrap.Component
29375  * Bootstrap LocationPicker class
29376  * @cfg {Number} latitude Position when init default 0
29377  * @cfg {Number} longitude Position when init default 0
29378  * @cfg {Number} zoom default 15
29379  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29380  * @cfg {Boolean} mapTypeControl default false
29381  * @cfg {Boolean} disableDoubleClickZoom default false
29382  * @cfg {Boolean} scrollwheel default true
29383  * @cfg {Boolean} streetViewControl default false
29384  * @cfg {Number} radius default 0
29385  * @cfg {String} locationName
29386  * @cfg {Boolean} draggable default true
29387  * @cfg {Boolean} enableAutocomplete default false
29388  * @cfg {Boolean} enableReverseGeocode default true
29389  * @cfg {String} markerTitle
29390  * 
29391  * @constructor
29392  * Create a new LocationPicker
29393  * @param {Object} config The config object
29394  */
29395
29396
29397 Roo.bootstrap.LocationPicker = function(config){
29398     
29399     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29400     
29401     this.addEvents({
29402         /**
29403          * @event initial
29404          * Fires when the picker initialized.
29405          * @param {Roo.bootstrap.LocationPicker} this
29406          * @param {Google Location} location
29407          */
29408         initial : true,
29409         /**
29410          * @event positionchanged
29411          * Fires when the picker position changed.
29412          * @param {Roo.bootstrap.LocationPicker} this
29413          * @param {Google Location} location
29414          */
29415         positionchanged : true,
29416         /**
29417          * @event resize
29418          * Fires when the map resize.
29419          * @param {Roo.bootstrap.LocationPicker} this
29420          */
29421         resize : true,
29422         /**
29423          * @event show
29424          * Fires when the map show.
29425          * @param {Roo.bootstrap.LocationPicker} this
29426          */
29427         show : true,
29428         /**
29429          * @event hide
29430          * Fires when the map hide.
29431          * @param {Roo.bootstrap.LocationPicker} this
29432          */
29433         hide : true,
29434         /**
29435          * @event mapClick
29436          * Fires when click the map.
29437          * @param {Roo.bootstrap.LocationPicker} this
29438          * @param {Map event} e
29439          */
29440         mapClick : true,
29441         /**
29442          * @event mapRightClick
29443          * Fires when right click the map.
29444          * @param {Roo.bootstrap.LocationPicker} this
29445          * @param {Map event} e
29446          */
29447         mapRightClick : true,
29448         /**
29449          * @event markerClick
29450          * Fires when click the marker.
29451          * @param {Roo.bootstrap.LocationPicker} this
29452          * @param {Map event} e
29453          */
29454         markerClick : true,
29455         /**
29456          * @event markerRightClick
29457          * Fires when right click the marker.
29458          * @param {Roo.bootstrap.LocationPicker} this
29459          * @param {Map event} e
29460          */
29461         markerRightClick : true,
29462         /**
29463          * @event OverlayViewDraw
29464          * Fires when OverlayView Draw
29465          * @param {Roo.bootstrap.LocationPicker} this
29466          */
29467         OverlayViewDraw : true,
29468         /**
29469          * @event OverlayViewOnAdd
29470          * Fires when OverlayView Draw
29471          * @param {Roo.bootstrap.LocationPicker} this
29472          */
29473         OverlayViewOnAdd : true,
29474         /**
29475          * @event OverlayViewOnRemove
29476          * Fires when OverlayView Draw
29477          * @param {Roo.bootstrap.LocationPicker} this
29478          */
29479         OverlayViewOnRemove : true,
29480         /**
29481          * @event OverlayViewShow
29482          * Fires when OverlayView Draw
29483          * @param {Roo.bootstrap.LocationPicker} this
29484          * @param {Pixel} cpx
29485          */
29486         OverlayViewShow : true,
29487         /**
29488          * @event OverlayViewHide
29489          * Fires when OverlayView Draw
29490          * @param {Roo.bootstrap.LocationPicker} this
29491          */
29492         OverlayViewHide : true,
29493         /**
29494          * @event loadexception
29495          * Fires when load google lib failed.
29496          * @param {Roo.bootstrap.LocationPicker} this
29497          */
29498         loadexception : true
29499     });
29500         
29501 };
29502
29503 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29504     
29505     gMapContext: false,
29506     
29507     latitude: 0,
29508     longitude: 0,
29509     zoom: 15,
29510     mapTypeId: false,
29511     mapTypeControl: false,
29512     disableDoubleClickZoom: false,
29513     scrollwheel: true,
29514     streetViewControl: false,
29515     radius: 0,
29516     locationName: '',
29517     draggable: true,
29518     enableAutocomplete: false,
29519     enableReverseGeocode: true,
29520     markerTitle: '',
29521     
29522     getAutoCreate: function()
29523     {
29524
29525         var cfg = {
29526             tag: 'div',
29527             cls: 'roo-location-picker'
29528         };
29529         
29530         return cfg
29531     },
29532     
29533     initEvents: function(ct, position)
29534     {       
29535         if(!this.el.getWidth() || this.isApplied()){
29536             return;
29537         }
29538         
29539         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29540         
29541         this.initial();
29542     },
29543     
29544     initial: function()
29545     {
29546         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29547             this.fireEvent('loadexception', this);
29548             return;
29549         }
29550         
29551         if(!this.mapTypeId){
29552             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29553         }
29554         
29555         this.gMapContext = this.GMapContext();
29556         
29557         this.initOverlayView();
29558         
29559         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29560         
29561         var _this = this;
29562                 
29563         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29564             _this.setPosition(_this.gMapContext.marker.position);
29565         });
29566         
29567         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29568             _this.fireEvent('mapClick', this, event);
29569             
29570         });
29571
29572         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29573             _this.fireEvent('mapRightClick', this, event);
29574             
29575         });
29576         
29577         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29578             _this.fireEvent('markerClick', this, event);
29579             
29580         });
29581
29582         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29583             _this.fireEvent('markerRightClick', this, event);
29584             
29585         });
29586         
29587         this.setPosition(this.gMapContext.location);
29588         
29589         this.fireEvent('initial', this, this.gMapContext.location);
29590     },
29591     
29592     initOverlayView: function()
29593     {
29594         var _this = this;
29595         
29596         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29597             
29598             draw: function()
29599             {
29600                 _this.fireEvent('OverlayViewDraw', _this);
29601             },
29602             
29603             onAdd: function()
29604             {
29605                 _this.fireEvent('OverlayViewOnAdd', _this);
29606             },
29607             
29608             onRemove: function()
29609             {
29610                 _this.fireEvent('OverlayViewOnRemove', _this);
29611             },
29612             
29613             show: function(cpx)
29614             {
29615                 _this.fireEvent('OverlayViewShow', _this, cpx);
29616             },
29617             
29618             hide: function()
29619             {
29620                 _this.fireEvent('OverlayViewHide', _this);
29621             }
29622             
29623         });
29624     },
29625     
29626     fromLatLngToContainerPixel: function(event)
29627     {
29628         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29629     },
29630     
29631     isApplied: function() 
29632     {
29633         return this.getGmapContext() == false ? false : true;
29634     },
29635     
29636     getGmapContext: function() 
29637     {
29638         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29639     },
29640     
29641     GMapContext: function() 
29642     {
29643         var position = new google.maps.LatLng(this.latitude, this.longitude);
29644         
29645         var _map = new google.maps.Map(this.el.dom, {
29646             center: position,
29647             zoom: this.zoom,
29648             mapTypeId: this.mapTypeId,
29649             mapTypeControl: this.mapTypeControl,
29650             disableDoubleClickZoom: this.disableDoubleClickZoom,
29651             scrollwheel: this.scrollwheel,
29652             streetViewControl: this.streetViewControl,
29653             locationName: this.locationName,
29654             draggable: this.draggable,
29655             enableAutocomplete: this.enableAutocomplete,
29656             enableReverseGeocode: this.enableReverseGeocode
29657         });
29658         
29659         var _marker = new google.maps.Marker({
29660             position: position,
29661             map: _map,
29662             title: this.markerTitle,
29663             draggable: this.draggable
29664         });
29665         
29666         return {
29667             map: _map,
29668             marker: _marker,
29669             circle: null,
29670             location: position,
29671             radius: this.radius,
29672             locationName: this.locationName,
29673             addressComponents: {
29674                 formatted_address: null,
29675                 addressLine1: null,
29676                 addressLine2: null,
29677                 streetName: null,
29678                 streetNumber: null,
29679                 city: null,
29680                 district: null,
29681                 state: null,
29682                 stateOrProvince: null
29683             },
29684             settings: this,
29685             domContainer: this.el.dom,
29686             geodecoder: new google.maps.Geocoder()
29687         };
29688     },
29689     
29690     drawCircle: function(center, radius, options) 
29691     {
29692         if (this.gMapContext.circle != null) {
29693             this.gMapContext.circle.setMap(null);
29694         }
29695         if (radius > 0) {
29696             radius *= 1;
29697             options = Roo.apply({}, options, {
29698                 strokeColor: "#0000FF",
29699                 strokeOpacity: .35,
29700                 strokeWeight: 2,
29701                 fillColor: "#0000FF",
29702                 fillOpacity: .2
29703             });
29704             
29705             options.map = this.gMapContext.map;
29706             options.radius = radius;
29707             options.center = center;
29708             this.gMapContext.circle = new google.maps.Circle(options);
29709             return this.gMapContext.circle;
29710         }
29711         
29712         return null;
29713     },
29714     
29715     setPosition: function(location) 
29716     {
29717         this.gMapContext.location = location;
29718         this.gMapContext.marker.setPosition(location);
29719         this.gMapContext.map.panTo(location);
29720         this.drawCircle(location, this.gMapContext.radius, {});
29721         
29722         var _this = this;
29723         
29724         if (this.gMapContext.settings.enableReverseGeocode) {
29725             this.gMapContext.geodecoder.geocode({
29726                 latLng: this.gMapContext.location
29727             }, function(results, status) {
29728                 
29729                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29730                     _this.gMapContext.locationName = results[0].formatted_address;
29731                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29732                     
29733                     _this.fireEvent('positionchanged', this, location);
29734                 }
29735             });
29736             
29737             return;
29738         }
29739         
29740         this.fireEvent('positionchanged', this, location);
29741     },
29742     
29743     resize: function()
29744     {
29745         google.maps.event.trigger(this.gMapContext.map, "resize");
29746         
29747         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29748         
29749         this.fireEvent('resize', this);
29750     },
29751     
29752     setPositionByLatLng: function(latitude, longitude)
29753     {
29754         this.setPosition(new google.maps.LatLng(latitude, longitude));
29755     },
29756     
29757     getCurrentPosition: function() 
29758     {
29759         return {
29760             latitude: this.gMapContext.location.lat(),
29761             longitude: this.gMapContext.location.lng()
29762         };
29763     },
29764     
29765     getAddressName: function() 
29766     {
29767         return this.gMapContext.locationName;
29768     },
29769     
29770     getAddressComponents: function() 
29771     {
29772         return this.gMapContext.addressComponents;
29773     },
29774     
29775     address_component_from_google_geocode: function(address_components) 
29776     {
29777         var result = {};
29778         
29779         for (var i = 0; i < address_components.length; i++) {
29780             var component = address_components[i];
29781             if (component.types.indexOf("postal_code") >= 0) {
29782                 result.postalCode = component.short_name;
29783             } else if (component.types.indexOf("street_number") >= 0) {
29784                 result.streetNumber = component.short_name;
29785             } else if (component.types.indexOf("route") >= 0) {
29786                 result.streetName = component.short_name;
29787             } else if (component.types.indexOf("neighborhood") >= 0) {
29788                 result.city = component.short_name;
29789             } else if (component.types.indexOf("locality") >= 0) {
29790                 result.city = component.short_name;
29791             } else if (component.types.indexOf("sublocality") >= 0) {
29792                 result.district = component.short_name;
29793             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29794                 result.stateOrProvince = component.short_name;
29795             } else if (component.types.indexOf("country") >= 0) {
29796                 result.country = component.short_name;
29797             }
29798         }
29799         
29800         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29801         result.addressLine2 = "";
29802         return result;
29803     },
29804     
29805     setZoomLevel: function(zoom)
29806     {
29807         this.gMapContext.map.setZoom(zoom);
29808     },
29809     
29810     show: function()
29811     {
29812         if(!this.el){
29813             return;
29814         }
29815         
29816         this.el.show();
29817         
29818         this.resize();
29819         
29820         this.fireEvent('show', this);
29821     },
29822     
29823     hide: function()
29824     {
29825         if(!this.el){
29826             return;
29827         }
29828         
29829         this.el.hide();
29830         
29831         this.fireEvent('hide', this);
29832     }
29833     
29834 });
29835
29836 Roo.apply(Roo.bootstrap.LocationPicker, {
29837     
29838     OverlayView : function(map, options)
29839     {
29840         options = options || {};
29841         
29842         this.setMap(map);
29843     }
29844     
29845     
29846 });/**
29847  * @class Roo.bootstrap.Alert
29848  * @extends Roo.bootstrap.Component
29849  * Bootstrap Alert class - shows an alert area box
29850  * eg
29851  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29852   Enter a valid email address
29853 </div>
29854  * @licence LGPL
29855  * @cfg {String} title The title of alert
29856  * @cfg {String} html The content of alert
29857  * @cfg {String} weight (  success | info | warning | danger )
29858  * @cfg {String} fa font-awesomeicon
29859  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29860  * @cfg {Boolean} close true to show a x closer
29861  * 
29862  * 
29863  * @constructor
29864  * Create a new alert
29865  * @param {Object} config The config object
29866  */
29867
29868
29869 Roo.bootstrap.Alert = function(config){
29870     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29871     
29872 };
29873
29874 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29875     
29876     title: '',
29877     html: '',
29878     weight: false,
29879     fa: false,
29880     faicon: false, // BC
29881     close : false,
29882     
29883     
29884     getAutoCreate : function()
29885     {
29886         
29887         var cfg = {
29888             tag : 'div',
29889             cls : 'alert',
29890             cn : [
29891                 {
29892                     tag: 'button',
29893                     type :  "button",
29894                     cls: "close",
29895                     html : '×',
29896                     style : this.close ? '' : 'display:none'
29897                 },
29898                 {
29899                     tag : 'i',
29900                     cls : 'roo-alert-icon'
29901                     
29902                 },
29903                 {
29904                     tag : 'b',
29905                     cls : 'roo-alert-title',
29906                     html : this.title
29907                 },
29908                 {
29909                     tag : 'span',
29910                     cls : 'roo-alert-text',
29911                     html : this.html
29912                 }
29913             ]
29914         };
29915         
29916         if(this.faicon){
29917             cfg.cn[0].cls += ' fa ' + this.faicon;
29918         }
29919         if(this.fa){
29920             cfg.cn[0].cls += ' fa ' + this.fa;
29921         }
29922         
29923         if(this.weight){
29924             cfg.cls += ' alert-' + this.weight;
29925         }
29926         
29927         return cfg;
29928     },
29929     
29930     initEvents: function() 
29931     {
29932         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29933         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29934         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29935         if (this.seconds > 0) {
29936             this.hide.defer(this.seconds, this);
29937         }
29938     },
29939     
29940     setTitle : function(str)
29941     {
29942         this.titleEl.dom.innerHTML = str;
29943     },
29944     
29945     setText : function(str)
29946     {
29947         this.titleEl.dom.innerHTML = str;
29948     },
29949     
29950     setWeight : function(weight)
29951     {
29952         if(this.weight){
29953             this.el.removeClass('alert-' + this.weight);
29954         }
29955         
29956         this.weight = weight;
29957         
29958         this.el.addClass('alert-' + this.weight);
29959     },
29960     
29961     setIcon : function(icon)
29962     {
29963         if(this.faicon){
29964             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29965         }
29966         
29967         this.faicon = icon;
29968         
29969         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29970     },
29971     
29972     hide: function() 
29973     {
29974         this.el.hide();   
29975     },
29976     
29977     show: function() 
29978     {  
29979         this.el.show();   
29980     }
29981     
29982 });
29983
29984  
29985 /*
29986 * Licence: LGPL
29987 */
29988
29989 /**
29990  * @class Roo.bootstrap.UploadCropbox
29991  * @extends Roo.bootstrap.Component
29992  * Bootstrap UploadCropbox class
29993  * @cfg {String} emptyText show when image has been loaded
29994  * @cfg {String} rotateNotify show when image too small to rotate
29995  * @cfg {Number} errorTimeout default 3000
29996  * @cfg {Number} minWidth default 300
29997  * @cfg {Number} minHeight default 300
29998  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29999  * @cfg {Boolean} isDocument (true|false) default false
30000  * @cfg {String} url action url
30001  * @cfg {String} paramName default 'imageUpload'
30002  * @cfg {String} method default POST
30003  * @cfg {Boolean} loadMask (true|false) default true
30004  * @cfg {Boolean} loadingText default 'Loading...'
30005  * 
30006  * @constructor
30007  * Create a new UploadCropbox
30008  * @param {Object} config The config object
30009  */
30010
30011 Roo.bootstrap.UploadCropbox = function(config){
30012     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30013     
30014     this.addEvents({
30015         /**
30016          * @event beforeselectfile
30017          * Fire before select file
30018          * @param {Roo.bootstrap.UploadCropbox} this
30019          */
30020         "beforeselectfile" : true,
30021         /**
30022          * @event initial
30023          * Fire after initEvent
30024          * @param {Roo.bootstrap.UploadCropbox} this
30025          */
30026         "initial" : true,
30027         /**
30028          * @event crop
30029          * Fire after initEvent
30030          * @param {Roo.bootstrap.UploadCropbox} this
30031          * @param {String} data
30032          */
30033         "crop" : true,
30034         /**
30035          * @event prepare
30036          * Fire when preparing the file data
30037          * @param {Roo.bootstrap.UploadCropbox} this
30038          * @param {Object} file
30039          */
30040         "prepare" : true,
30041         /**
30042          * @event exception
30043          * Fire when get exception
30044          * @param {Roo.bootstrap.UploadCropbox} this
30045          * @param {XMLHttpRequest} xhr
30046          */
30047         "exception" : true,
30048         /**
30049          * @event beforeloadcanvas
30050          * Fire before load the canvas
30051          * @param {Roo.bootstrap.UploadCropbox} this
30052          * @param {String} src
30053          */
30054         "beforeloadcanvas" : true,
30055         /**
30056          * @event trash
30057          * Fire when trash image
30058          * @param {Roo.bootstrap.UploadCropbox} this
30059          */
30060         "trash" : true,
30061         /**
30062          * @event download
30063          * Fire when download the image
30064          * @param {Roo.bootstrap.UploadCropbox} this
30065          */
30066         "download" : true,
30067         /**
30068          * @event footerbuttonclick
30069          * Fire when footerbuttonclick
30070          * @param {Roo.bootstrap.UploadCropbox} this
30071          * @param {String} type
30072          */
30073         "footerbuttonclick" : true,
30074         /**
30075          * @event resize
30076          * Fire when resize
30077          * @param {Roo.bootstrap.UploadCropbox} this
30078          */
30079         "resize" : true,
30080         /**
30081          * @event rotate
30082          * Fire when rotate the image
30083          * @param {Roo.bootstrap.UploadCropbox} this
30084          * @param {String} pos
30085          */
30086         "rotate" : true,
30087         /**
30088          * @event inspect
30089          * Fire when inspect the file
30090          * @param {Roo.bootstrap.UploadCropbox} this
30091          * @param {Object} file
30092          */
30093         "inspect" : true,
30094         /**
30095          * @event upload
30096          * Fire when xhr upload the file
30097          * @param {Roo.bootstrap.UploadCropbox} this
30098          * @param {Object} data
30099          */
30100         "upload" : true,
30101         /**
30102          * @event arrange
30103          * Fire when arrange the file data
30104          * @param {Roo.bootstrap.UploadCropbox} this
30105          * @param {Object} formData
30106          */
30107         "arrange" : true
30108     });
30109     
30110     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30111 };
30112
30113 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30114     
30115     emptyText : 'Click to upload image',
30116     rotateNotify : 'Image is too small to rotate',
30117     errorTimeout : 3000,
30118     scale : 0,
30119     baseScale : 1,
30120     rotate : 0,
30121     dragable : false,
30122     pinching : false,
30123     mouseX : 0,
30124     mouseY : 0,
30125     cropData : false,
30126     minWidth : 300,
30127     minHeight : 300,
30128     file : false,
30129     exif : {},
30130     baseRotate : 1,
30131     cropType : 'image/jpeg',
30132     buttons : false,
30133     canvasLoaded : false,
30134     isDocument : false,
30135     method : 'POST',
30136     paramName : 'imageUpload',
30137     loadMask : true,
30138     loadingText : 'Loading...',
30139     maskEl : false,
30140     
30141     getAutoCreate : function()
30142     {
30143         var cfg = {
30144             tag : 'div',
30145             cls : 'roo-upload-cropbox',
30146             cn : [
30147                 {
30148                     tag : 'input',
30149                     cls : 'roo-upload-cropbox-selector',
30150                     type : 'file'
30151                 },
30152                 {
30153                     tag : 'div',
30154                     cls : 'roo-upload-cropbox-body',
30155                     style : 'cursor:pointer',
30156                     cn : [
30157                         {
30158                             tag : 'div',
30159                             cls : 'roo-upload-cropbox-preview'
30160                         },
30161                         {
30162                             tag : 'div',
30163                             cls : 'roo-upload-cropbox-thumb'
30164                         },
30165                         {
30166                             tag : 'div',
30167                             cls : 'roo-upload-cropbox-empty-notify',
30168                             html : this.emptyText
30169                         },
30170                         {
30171                             tag : 'div',
30172                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30173                             html : this.rotateNotify
30174                         }
30175                     ]
30176                 },
30177                 {
30178                     tag : 'div',
30179                     cls : 'roo-upload-cropbox-footer',
30180                     cn : {
30181                         tag : 'div',
30182                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30183                         cn : []
30184                     }
30185                 }
30186             ]
30187         };
30188         
30189         return cfg;
30190     },
30191     
30192     onRender : function(ct, position)
30193     {
30194         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30195         
30196         if (this.buttons.length) {
30197             
30198             Roo.each(this.buttons, function(bb) {
30199                 
30200                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30201                 
30202                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30203                 
30204             }, this);
30205         }
30206         
30207         if(this.loadMask){
30208             this.maskEl = this.el;
30209         }
30210     },
30211     
30212     initEvents : function()
30213     {
30214         this.urlAPI = (window.createObjectURL && window) || 
30215                                 (window.URL && URL.revokeObjectURL && URL) || 
30216                                 (window.webkitURL && webkitURL);
30217                         
30218         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30219         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30220         
30221         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30222         this.selectorEl.hide();
30223         
30224         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30225         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30226         
30227         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30228         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30229         this.thumbEl.hide();
30230         
30231         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30232         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30233         
30234         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30235         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30236         this.errorEl.hide();
30237         
30238         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30239         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240         this.footerEl.hide();
30241         
30242         this.setThumbBoxSize();
30243         
30244         this.bind();
30245         
30246         this.resize();
30247         
30248         this.fireEvent('initial', this);
30249     },
30250
30251     bind : function()
30252     {
30253         var _this = this;
30254         
30255         window.addEventListener("resize", function() { _this.resize(); } );
30256         
30257         this.bodyEl.on('click', this.beforeSelectFile, this);
30258         
30259         if(Roo.isTouch){
30260             this.bodyEl.on('touchstart', this.onTouchStart, this);
30261             this.bodyEl.on('touchmove', this.onTouchMove, this);
30262             this.bodyEl.on('touchend', this.onTouchEnd, this);
30263         }
30264         
30265         if(!Roo.isTouch){
30266             this.bodyEl.on('mousedown', this.onMouseDown, this);
30267             this.bodyEl.on('mousemove', this.onMouseMove, this);
30268             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30269             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30270             Roo.get(document).on('mouseup', this.onMouseUp, this);
30271         }
30272         
30273         this.selectorEl.on('change', this.onFileSelected, this);
30274     },
30275     
30276     reset : function()
30277     {    
30278         this.scale = 0;
30279         this.baseScale = 1;
30280         this.rotate = 0;
30281         this.baseRotate = 1;
30282         this.dragable = false;
30283         this.pinching = false;
30284         this.mouseX = 0;
30285         this.mouseY = 0;
30286         this.cropData = false;
30287         this.notifyEl.dom.innerHTML = this.emptyText;
30288         
30289         this.selectorEl.dom.value = '';
30290         
30291     },
30292     
30293     resize : function()
30294     {
30295         if(this.fireEvent('resize', this) != false){
30296             this.setThumbBoxPosition();
30297             this.setCanvasPosition();
30298         }
30299     },
30300     
30301     onFooterButtonClick : function(e, el, o, type)
30302     {
30303         switch (type) {
30304             case 'rotate-left' :
30305                 this.onRotateLeft(e);
30306                 break;
30307             case 'rotate-right' :
30308                 this.onRotateRight(e);
30309                 break;
30310             case 'picture' :
30311                 this.beforeSelectFile(e);
30312                 break;
30313             case 'trash' :
30314                 this.trash(e);
30315                 break;
30316             case 'crop' :
30317                 this.crop(e);
30318                 break;
30319             case 'download' :
30320                 this.download(e);
30321                 break;
30322             default :
30323                 break;
30324         }
30325         
30326         this.fireEvent('footerbuttonclick', this, type);
30327     },
30328     
30329     beforeSelectFile : function(e)
30330     {
30331         e.preventDefault();
30332         
30333         if(this.fireEvent('beforeselectfile', this) != false){
30334             this.selectorEl.dom.click();
30335         }
30336     },
30337     
30338     onFileSelected : function(e)
30339     {
30340         e.preventDefault();
30341         
30342         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30343             return;
30344         }
30345         
30346         var file = this.selectorEl.dom.files[0];
30347         
30348         if(this.fireEvent('inspect', this, file) != false){
30349             this.prepare(file);
30350         }
30351         
30352     },
30353     
30354     trash : function(e)
30355     {
30356         this.fireEvent('trash', this);
30357     },
30358     
30359     download : function(e)
30360     {
30361         this.fireEvent('download', this);
30362     },
30363     
30364     loadCanvas : function(src)
30365     {   
30366         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30367             
30368             this.reset();
30369             
30370             this.imageEl = document.createElement('img');
30371             
30372             var _this = this;
30373             
30374             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30375             
30376             this.imageEl.src = src;
30377         }
30378     },
30379     
30380     onLoadCanvas : function()
30381     {   
30382         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30383         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30384         
30385         this.bodyEl.un('click', this.beforeSelectFile, this);
30386         
30387         this.notifyEl.hide();
30388         this.thumbEl.show();
30389         this.footerEl.show();
30390         
30391         this.baseRotateLevel();
30392         
30393         if(this.isDocument){
30394             this.setThumbBoxSize();
30395         }
30396         
30397         this.setThumbBoxPosition();
30398         
30399         this.baseScaleLevel();
30400         
30401         this.draw();
30402         
30403         this.resize();
30404         
30405         this.canvasLoaded = true;
30406         
30407         if(this.loadMask){
30408             this.maskEl.unmask();
30409         }
30410         
30411     },
30412     
30413     setCanvasPosition : function()
30414     {   
30415         if(!this.canvasEl){
30416             return;
30417         }
30418         
30419         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30420         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30421         
30422         this.previewEl.setLeft(pw);
30423         this.previewEl.setTop(ph);
30424         
30425     },
30426     
30427     onMouseDown : function(e)
30428     {   
30429         e.stopEvent();
30430         
30431         this.dragable = true;
30432         this.pinching = false;
30433         
30434         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30435             this.dragable = false;
30436             return;
30437         }
30438         
30439         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30440         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30441         
30442     },
30443     
30444     onMouseMove : function(e)
30445     {   
30446         e.stopEvent();
30447         
30448         if(!this.canvasLoaded){
30449             return;
30450         }
30451         
30452         if (!this.dragable){
30453             return;
30454         }
30455         
30456         var minX = Math.ceil(this.thumbEl.getLeft(true));
30457         var minY = Math.ceil(this.thumbEl.getTop(true));
30458         
30459         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30460         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30461         
30462         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30463         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30464         
30465         x = x - this.mouseX;
30466         y = y - this.mouseY;
30467         
30468         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30469         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30470         
30471         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30472         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30473         
30474         this.previewEl.setLeft(bgX);
30475         this.previewEl.setTop(bgY);
30476         
30477         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30478         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30479     },
30480     
30481     onMouseUp : function(e)
30482     {   
30483         e.stopEvent();
30484         
30485         this.dragable = false;
30486     },
30487     
30488     onMouseWheel : function(e)
30489     {   
30490         e.stopEvent();
30491         
30492         this.startScale = this.scale;
30493         
30494         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30495         
30496         if(!this.zoomable()){
30497             this.scale = this.startScale;
30498             return;
30499         }
30500         
30501         this.draw();
30502         
30503         return;
30504     },
30505     
30506     zoomable : function()
30507     {
30508         var minScale = this.thumbEl.getWidth() / this.minWidth;
30509         
30510         if(this.minWidth < this.minHeight){
30511             minScale = this.thumbEl.getHeight() / this.minHeight;
30512         }
30513         
30514         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30515         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30516         
30517         if(
30518                 this.isDocument &&
30519                 (this.rotate == 0 || this.rotate == 180) && 
30520                 (
30521                     width > this.imageEl.OriginWidth || 
30522                     height > this.imageEl.OriginHeight ||
30523                     (width < this.minWidth && height < this.minHeight)
30524                 )
30525         ){
30526             return false;
30527         }
30528         
30529         if(
30530                 this.isDocument &&
30531                 (this.rotate == 90 || this.rotate == 270) && 
30532                 (
30533                     width > this.imageEl.OriginWidth || 
30534                     height > this.imageEl.OriginHeight ||
30535                     (width < this.minHeight && height < this.minWidth)
30536                 )
30537         ){
30538             return false;
30539         }
30540         
30541         if(
30542                 !this.isDocument &&
30543                 (this.rotate == 0 || this.rotate == 180) && 
30544                 (
30545                     width < this.minWidth || 
30546                     width > this.imageEl.OriginWidth || 
30547                     height < this.minHeight || 
30548                     height > this.imageEl.OriginHeight
30549                 )
30550         ){
30551             return false;
30552         }
30553         
30554         if(
30555                 !this.isDocument &&
30556                 (this.rotate == 90 || this.rotate == 270) && 
30557                 (
30558                     width < this.minHeight || 
30559                     width > this.imageEl.OriginWidth || 
30560                     height < this.minWidth || 
30561                     height > this.imageEl.OriginHeight
30562                 )
30563         ){
30564             return false;
30565         }
30566         
30567         return true;
30568         
30569     },
30570     
30571     onRotateLeft : function(e)
30572     {   
30573         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30574             
30575             var minScale = this.thumbEl.getWidth() / this.minWidth;
30576             
30577             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30578             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30579             
30580             this.startScale = this.scale;
30581             
30582             while (this.getScaleLevel() < minScale){
30583             
30584                 this.scale = this.scale + 1;
30585                 
30586                 if(!this.zoomable()){
30587                     break;
30588                 }
30589                 
30590                 if(
30591                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30592                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30593                 ){
30594                     continue;
30595                 }
30596                 
30597                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30598
30599                 this.draw();
30600                 
30601                 return;
30602             }
30603             
30604             this.scale = this.startScale;
30605             
30606             this.onRotateFail();
30607             
30608             return false;
30609         }
30610         
30611         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30612
30613         if(this.isDocument){
30614             this.setThumbBoxSize();
30615             this.setThumbBoxPosition();
30616             this.setCanvasPosition();
30617         }
30618         
30619         this.draw();
30620         
30621         this.fireEvent('rotate', this, 'left');
30622         
30623     },
30624     
30625     onRotateRight : function(e)
30626     {
30627         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30628             
30629             var minScale = this.thumbEl.getWidth() / this.minWidth;
30630         
30631             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30632             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30633             
30634             this.startScale = this.scale;
30635             
30636             while (this.getScaleLevel() < minScale){
30637             
30638                 this.scale = this.scale + 1;
30639                 
30640                 if(!this.zoomable()){
30641                     break;
30642                 }
30643                 
30644                 if(
30645                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30646                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30647                 ){
30648                     continue;
30649                 }
30650                 
30651                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30652
30653                 this.draw();
30654                 
30655                 return;
30656             }
30657             
30658             this.scale = this.startScale;
30659             
30660             this.onRotateFail();
30661             
30662             return false;
30663         }
30664         
30665         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30666
30667         if(this.isDocument){
30668             this.setThumbBoxSize();
30669             this.setThumbBoxPosition();
30670             this.setCanvasPosition();
30671         }
30672         
30673         this.draw();
30674         
30675         this.fireEvent('rotate', this, 'right');
30676     },
30677     
30678     onRotateFail : function()
30679     {
30680         this.errorEl.show(true);
30681         
30682         var _this = this;
30683         
30684         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30685     },
30686     
30687     draw : function()
30688     {
30689         this.previewEl.dom.innerHTML = '';
30690         
30691         var canvasEl = document.createElement("canvas");
30692         
30693         var contextEl = canvasEl.getContext("2d");
30694         
30695         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30696         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30697         var center = this.imageEl.OriginWidth / 2;
30698         
30699         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30700             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30701             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30702             center = this.imageEl.OriginHeight / 2;
30703         }
30704         
30705         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30706         
30707         contextEl.translate(center, center);
30708         contextEl.rotate(this.rotate * Math.PI / 180);
30709
30710         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30711         
30712         this.canvasEl = document.createElement("canvas");
30713         
30714         this.contextEl = this.canvasEl.getContext("2d");
30715         
30716         switch (this.rotate) {
30717             case 0 :
30718                 
30719                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30720                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30721                 
30722                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30723                 
30724                 break;
30725             case 90 : 
30726                 
30727                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30728                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30729                 
30730                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30731                     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);
30732                     break;
30733                 }
30734                 
30735                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30736                 
30737                 break;
30738             case 180 :
30739                 
30740                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30741                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30742                 
30743                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30744                     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);
30745                     break;
30746                 }
30747                 
30748                 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);
30749                 
30750                 break;
30751             case 270 :
30752                 
30753                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30754                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30755         
30756                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30757                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30758                     break;
30759                 }
30760                 
30761                 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);
30762                 
30763                 break;
30764             default : 
30765                 break;
30766         }
30767         
30768         this.previewEl.appendChild(this.canvasEl);
30769         
30770         this.setCanvasPosition();
30771     },
30772     
30773     crop : function()
30774     {
30775         if(!this.canvasLoaded){
30776             return;
30777         }
30778         
30779         var imageCanvas = document.createElement("canvas");
30780         
30781         var imageContext = imageCanvas.getContext("2d");
30782         
30783         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30784         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30785         
30786         var center = imageCanvas.width / 2;
30787         
30788         imageContext.translate(center, center);
30789         
30790         imageContext.rotate(this.rotate * Math.PI / 180);
30791         
30792         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30793         
30794         var canvas = document.createElement("canvas");
30795         
30796         var context = canvas.getContext("2d");
30797                 
30798         canvas.width = this.minWidth;
30799         canvas.height = this.minHeight;
30800
30801         switch (this.rotate) {
30802             case 0 :
30803                 
30804                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30805                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30806                 
30807                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30808                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30809                 
30810                 var targetWidth = this.minWidth - 2 * x;
30811                 var targetHeight = this.minHeight - 2 * y;
30812                 
30813                 var scale = 1;
30814                 
30815                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30816                     scale = targetWidth / width;
30817                 }
30818                 
30819                 if(x > 0 && y == 0){
30820                     scale = targetHeight / height;
30821                 }
30822                 
30823                 if(x > 0 && y > 0){
30824                     scale = targetWidth / width;
30825                     
30826                     if(width < height){
30827                         scale = targetHeight / height;
30828                     }
30829                 }
30830                 
30831                 context.scale(scale, scale);
30832                 
30833                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30834                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30835
30836                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30837                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30838
30839                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30840                 
30841                 break;
30842             case 90 : 
30843                 
30844                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30845                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30846                 
30847                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30848                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30849                 
30850                 var targetWidth = this.minWidth - 2 * x;
30851                 var targetHeight = this.minHeight - 2 * y;
30852                 
30853                 var scale = 1;
30854                 
30855                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30856                     scale = targetWidth / width;
30857                 }
30858                 
30859                 if(x > 0 && y == 0){
30860                     scale = targetHeight / height;
30861                 }
30862                 
30863                 if(x > 0 && y > 0){
30864                     scale = targetWidth / width;
30865                     
30866                     if(width < height){
30867                         scale = targetHeight / height;
30868                     }
30869                 }
30870                 
30871                 context.scale(scale, scale);
30872                 
30873                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30874                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30875
30876                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30877                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30878                 
30879                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30880                 
30881                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30882                 
30883                 break;
30884             case 180 :
30885                 
30886                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30887                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30888                 
30889                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30890                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30891                 
30892                 var targetWidth = this.minWidth - 2 * x;
30893                 var targetHeight = this.minHeight - 2 * y;
30894                 
30895                 var scale = 1;
30896                 
30897                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30898                     scale = targetWidth / width;
30899                 }
30900                 
30901                 if(x > 0 && y == 0){
30902                     scale = targetHeight / height;
30903                 }
30904                 
30905                 if(x > 0 && y > 0){
30906                     scale = targetWidth / width;
30907                     
30908                     if(width < height){
30909                         scale = targetHeight / height;
30910                     }
30911                 }
30912                 
30913                 context.scale(scale, scale);
30914                 
30915                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30916                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30917
30918                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30919                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30920
30921                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30922                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30923                 
30924                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30925                 
30926                 break;
30927             case 270 :
30928                 
30929                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30930                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30931                 
30932                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30933                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30934                 
30935                 var targetWidth = this.minWidth - 2 * x;
30936                 var targetHeight = this.minHeight - 2 * y;
30937                 
30938                 var scale = 1;
30939                 
30940                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30941                     scale = targetWidth / width;
30942                 }
30943                 
30944                 if(x > 0 && y == 0){
30945                     scale = targetHeight / height;
30946                 }
30947                 
30948                 if(x > 0 && y > 0){
30949                     scale = targetWidth / width;
30950                     
30951                     if(width < height){
30952                         scale = targetHeight / height;
30953                     }
30954                 }
30955                 
30956                 context.scale(scale, scale);
30957                 
30958                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30959                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30960
30961                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30962                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30963                 
30964                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30965                 
30966                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30967                 
30968                 break;
30969             default : 
30970                 break;
30971         }
30972         
30973         this.cropData = canvas.toDataURL(this.cropType);
30974         
30975         if(this.fireEvent('crop', this, this.cropData) !== false){
30976             this.process(this.file, this.cropData);
30977         }
30978         
30979         return;
30980         
30981     },
30982     
30983     setThumbBoxSize : function()
30984     {
30985         var width, height;
30986         
30987         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30988             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30989             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30990             
30991             this.minWidth = width;
30992             this.minHeight = height;
30993             
30994             if(this.rotate == 90 || this.rotate == 270){
30995                 this.minWidth = height;
30996                 this.minHeight = width;
30997             }
30998         }
30999         
31000         height = 300;
31001         width = Math.ceil(this.minWidth * height / this.minHeight);
31002         
31003         if(this.minWidth > this.minHeight){
31004             width = 300;
31005             height = Math.ceil(this.minHeight * width / this.minWidth);
31006         }
31007         
31008         this.thumbEl.setStyle({
31009             width : width + 'px',
31010             height : height + 'px'
31011         });
31012
31013         return;
31014             
31015     },
31016     
31017     setThumbBoxPosition : function()
31018     {
31019         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31020         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31021         
31022         this.thumbEl.setLeft(x);
31023         this.thumbEl.setTop(y);
31024         
31025     },
31026     
31027     baseRotateLevel : function()
31028     {
31029         this.baseRotate = 1;
31030         
31031         if(
31032                 typeof(this.exif) != 'undefined' &&
31033                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31034                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31035         ){
31036             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31037         }
31038         
31039         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31040         
31041     },
31042     
31043     baseScaleLevel : function()
31044     {
31045         var width, height;
31046         
31047         if(this.isDocument){
31048             
31049             if(this.baseRotate == 6 || this.baseRotate == 8){
31050             
31051                 height = this.thumbEl.getHeight();
31052                 this.baseScale = height / this.imageEl.OriginWidth;
31053
31054                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31055                     width = this.thumbEl.getWidth();
31056                     this.baseScale = width / this.imageEl.OriginHeight;
31057                 }
31058
31059                 return;
31060             }
31061
31062             height = this.thumbEl.getHeight();
31063             this.baseScale = height / this.imageEl.OriginHeight;
31064
31065             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31066                 width = this.thumbEl.getWidth();
31067                 this.baseScale = width / this.imageEl.OriginWidth;
31068             }
31069
31070             return;
31071         }
31072         
31073         if(this.baseRotate == 6 || this.baseRotate == 8){
31074             
31075             width = this.thumbEl.getHeight();
31076             this.baseScale = width / this.imageEl.OriginHeight;
31077             
31078             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31079                 height = this.thumbEl.getWidth();
31080                 this.baseScale = height / this.imageEl.OriginHeight;
31081             }
31082             
31083             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31084                 height = this.thumbEl.getWidth();
31085                 this.baseScale = height / this.imageEl.OriginHeight;
31086                 
31087                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31088                     width = this.thumbEl.getHeight();
31089                     this.baseScale = width / this.imageEl.OriginWidth;
31090                 }
31091             }
31092             
31093             return;
31094         }
31095         
31096         width = this.thumbEl.getWidth();
31097         this.baseScale = width / this.imageEl.OriginWidth;
31098         
31099         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31100             height = this.thumbEl.getHeight();
31101             this.baseScale = height / this.imageEl.OriginHeight;
31102         }
31103         
31104         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31105             
31106             height = this.thumbEl.getHeight();
31107             this.baseScale = height / this.imageEl.OriginHeight;
31108             
31109             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31110                 width = this.thumbEl.getWidth();
31111                 this.baseScale = width / this.imageEl.OriginWidth;
31112             }
31113             
31114         }
31115         
31116         return;
31117     },
31118     
31119     getScaleLevel : function()
31120     {
31121         return this.baseScale * Math.pow(1.1, this.scale);
31122     },
31123     
31124     onTouchStart : function(e)
31125     {
31126         if(!this.canvasLoaded){
31127             this.beforeSelectFile(e);
31128             return;
31129         }
31130         
31131         var touches = e.browserEvent.touches;
31132         
31133         if(!touches){
31134             return;
31135         }
31136         
31137         if(touches.length == 1){
31138             this.onMouseDown(e);
31139             return;
31140         }
31141         
31142         if(touches.length != 2){
31143             return;
31144         }
31145         
31146         var coords = [];
31147         
31148         for(var i = 0, finger; finger = touches[i]; i++){
31149             coords.push(finger.pageX, finger.pageY);
31150         }
31151         
31152         var x = Math.pow(coords[0] - coords[2], 2);
31153         var y = Math.pow(coords[1] - coords[3], 2);
31154         
31155         this.startDistance = Math.sqrt(x + y);
31156         
31157         this.startScale = this.scale;
31158         
31159         this.pinching = true;
31160         this.dragable = false;
31161         
31162     },
31163     
31164     onTouchMove : function(e)
31165     {
31166         if(!this.pinching && !this.dragable){
31167             return;
31168         }
31169         
31170         var touches = e.browserEvent.touches;
31171         
31172         if(!touches){
31173             return;
31174         }
31175         
31176         if(this.dragable){
31177             this.onMouseMove(e);
31178             return;
31179         }
31180         
31181         var coords = [];
31182         
31183         for(var i = 0, finger; finger = touches[i]; i++){
31184             coords.push(finger.pageX, finger.pageY);
31185         }
31186         
31187         var x = Math.pow(coords[0] - coords[2], 2);
31188         var y = Math.pow(coords[1] - coords[3], 2);
31189         
31190         this.endDistance = Math.sqrt(x + y);
31191         
31192         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31193         
31194         if(!this.zoomable()){
31195             this.scale = this.startScale;
31196             return;
31197         }
31198         
31199         this.draw();
31200         
31201     },
31202     
31203     onTouchEnd : function(e)
31204     {
31205         this.pinching = false;
31206         this.dragable = false;
31207         
31208     },
31209     
31210     process : function(file, crop)
31211     {
31212         if(this.loadMask){
31213             this.maskEl.mask(this.loadingText);
31214         }
31215         
31216         this.xhr = new XMLHttpRequest();
31217         
31218         file.xhr = this.xhr;
31219
31220         this.xhr.open(this.method, this.url, true);
31221         
31222         var headers = {
31223             "Accept": "application/json",
31224             "Cache-Control": "no-cache",
31225             "X-Requested-With": "XMLHttpRequest"
31226         };
31227         
31228         for (var headerName in headers) {
31229             var headerValue = headers[headerName];
31230             if (headerValue) {
31231                 this.xhr.setRequestHeader(headerName, headerValue);
31232             }
31233         }
31234         
31235         var _this = this;
31236         
31237         this.xhr.onload = function()
31238         {
31239             _this.xhrOnLoad(_this.xhr);
31240         }
31241         
31242         this.xhr.onerror = function()
31243         {
31244             _this.xhrOnError(_this.xhr);
31245         }
31246         
31247         var formData = new FormData();
31248
31249         formData.append('returnHTML', 'NO');
31250         
31251         if(crop){
31252             formData.append('crop', crop);
31253         }
31254         
31255         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31256             formData.append(this.paramName, file, file.name);
31257         }
31258         
31259         if(typeof(file.filename) != 'undefined'){
31260             formData.append('filename', file.filename);
31261         }
31262         
31263         if(typeof(file.mimetype) != 'undefined'){
31264             formData.append('mimetype', file.mimetype);
31265         }
31266         
31267         if(this.fireEvent('arrange', this, formData) != false){
31268             this.xhr.send(formData);
31269         };
31270     },
31271     
31272     xhrOnLoad : function(xhr)
31273     {
31274         if(this.loadMask){
31275             this.maskEl.unmask();
31276         }
31277         
31278         if (xhr.readyState !== 4) {
31279             this.fireEvent('exception', this, xhr);
31280             return;
31281         }
31282
31283         var response = Roo.decode(xhr.responseText);
31284         
31285         if(!response.success){
31286             this.fireEvent('exception', this, xhr);
31287             return;
31288         }
31289         
31290         var response = Roo.decode(xhr.responseText);
31291         
31292         this.fireEvent('upload', this, response);
31293         
31294     },
31295     
31296     xhrOnError : function()
31297     {
31298         if(this.loadMask){
31299             this.maskEl.unmask();
31300         }
31301         
31302         Roo.log('xhr on error');
31303         
31304         var response = Roo.decode(xhr.responseText);
31305           
31306         Roo.log(response);
31307         
31308     },
31309     
31310     prepare : function(file)
31311     {   
31312         if(this.loadMask){
31313             this.maskEl.mask(this.loadingText);
31314         }
31315         
31316         this.file = false;
31317         this.exif = {};
31318         
31319         if(typeof(file) === 'string'){
31320             this.loadCanvas(file);
31321             return;
31322         }
31323         
31324         if(!file || !this.urlAPI){
31325             return;
31326         }
31327         
31328         this.file = file;
31329         this.cropType = file.type;
31330         
31331         var _this = this;
31332         
31333         if(this.fireEvent('prepare', this, this.file) != false){
31334             
31335             var reader = new FileReader();
31336             
31337             reader.onload = function (e) {
31338                 if (e.target.error) {
31339                     Roo.log(e.target.error);
31340                     return;
31341                 }
31342                 
31343                 var buffer = e.target.result,
31344                     dataView = new DataView(buffer),
31345                     offset = 2,
31346                     maxOffset = dataView.byteLength - 4,
31347                     markerBytes,
31348                     markerLength;
31349                 
31350                 if (dataView.getUint16(0) === 0xffd8) {
31351                     while (offset < maxOffset) {
31352                         markerBytes = dataView.getUint16(offset);
31353                         
31354                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31355                             markerLength = dataView.getUint16(offset + 2) + 2;
31356                             if (offset + markerLength > dataView.byteLength) {
31357                                 Roo.log('Invalid meta data: Invalid segment size.');
31358                                 break;
31359                             }
31360                             
31361                             if(markerBytes == 0xffe1){
31362                                 _this.parseExifData(
31363                                     dataView,
31364                                     offset,
31365                                     markerLength
31366                                 );
31367                             }
31368                             
31369                             offset += markerLength;
31370                             
31371                             continue;
31372                         }
31373                         
31374                         break;
31375                     }
31376                     
31377                 }
31378                 
31379                 var url = _this.urlAPI.createObjectURL(_this.file);
31380                 
31381                 _this.loadCanvas(url);
31382                 
31383                 return;
31384             }
31385             
31386             reader.readAsArrayBuffer(this.file);
31387             
31388         }
31389         
31390     },
31391     
31392     parseExifData : function(dataView, offset, length)
31393     {
31394         var tiffOffset = offset + 10,
31395             littleEndian,
31396             dirOffset;
31397     
31398         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31399             // No Exif data, might be XMP data instead
31400             return;
31401         }
31402         
31403         // Check for the ASCII code for "Exif" (0x45786966):
31404         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31405             // No Exif data, might be XMP data instead
31406             return;
31407         }
31408         if (tiffOffset + 8 > dataView.byteLength) {
31409             Roo.log('Invalid Exif data: Invalid segment size.');
31410             return;
31411         }
31412         // Check for the two null bytes:
31413         if (dataView.getUint16(offset + 8) !== 0x0000) {
31414             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31415             return;
31416         }
31417         // Check the byte alignment:
31418         switch (dataView.getUint16(tiffOffset)) {
31419         case 0x4949:
31420             littleEndian = true;
31421             break;
31422         case 0x4D4D:
31423             littleEndian = false;
31424             break;
31425         default:
31426             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31427             return;
31428         }
31429         // Check for the TIFF tag marker (0x002A):
31430         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31431             Roo.log('Invalid Exif data: Missing TIFF marker.');
31432             return;
31433         }
31434         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31435         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31436         
31437         this.parseExifTags(
31438             dataView,
31439             tiffOffset,
31440             tiffOffset + dirOffset,
31441             littleEndian
31442         );
31443     },
31444     
31445     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31446     {
31447         var tagsNumber,
31448             dirEndOffset,
31449             i;
31450         if (dirOffset + 6 > dataView.byteLength) {
31451             Roo.log('Invalid Exif data: Invalid directory offset.');
31452             return;
31453         }
31454         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31455         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31456         if (dirEndOffset + 4 > dataView.byteLength) {
31457             Roo.log('Invalid Exif data: Invalid directory size.');
31458             return;
31459         }
31460         for (i = 0; i < tagsNumber; i += 1) {
31461             this.parseExifTag(
31462                 dataView,
31463                 tiffOffset,
31464                 dirOffset + 2 + 12 * i, // tag offset
31465                 littleEndian
31466             );
31467         }
31468         // Return the offset to the next directory:
31469         return dataView.getUint32(dirEndOffset, littleEndian);
31470     },
31471     
31472     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31473     {
31474         var tag = dataView.getUint16(offset, littleEndian);
31475         
31476         this.exif[tag] = this.getExifValue(
31477             dataView,
31478             tiffOffset,
31479             offset,
31480             dataView.getUint16(offset + 2, littleEndian), // tag type
31481             dataView.getUint32(offset + 4, littleEndian), // tag length
31482             littleEndian
31483         );
31484     },
31485     
31486     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31487     {
31488         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31489             tagSize,
31490             dataOffset,
31491             values,
31492             i,
31493             str,
31494             c;
31495     
31496         if (!tagType) {
31497             Roo.log('Invalid Exif data: Invalid tag type.');
31498             return;
31499         }
31500         
31501         tagSize = tagType.size * length;
31502         // Determine if the value is contained in the dataOffset bytes,
31503         // or if the value at the dataOffset is a pointer to the actual data:
31504         dataOffset = tagSize > 4 ?
31505                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31506         if (dataOffset + tagSize > dataView.byteLength) {
31507             Roo.log('Invalid Exif data: Invalid data offset.');
31508             return;
31509         }
31510         if (length === 1) {
31511             return tagType.getValue(dataView, dataOffset, littleEndian);
31512         }
31513         values = [];
31514         for (i = 0; i < length; i += 1) {
31515             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31516         }
31517         
31518         if (tagType.ascii) {
31519             str = '';
31520             // Concatenate the chars:
31521             for (i = 0; i < values.length; i += 1) {
31522                 c = values[i];
31523                 // Ignore the terminating NULL byte(s):
31524                 if (c === '\u0000') {
31525                     break;
31526                 }
31527                 str += c;
31528             }
31529             return str;
31530         }
31531         return values;
31532     }
31533     
31534 });
31535
31536 Roo.apply(Roo.bootstrap.UploadCropbox, {
31537     tags : {
31538         'Orientation': 0x0112
31539     },
31540     
31541     Orientation: {
31542             1: 0, //'top-left',
31543 //            2: 'top-right',
31544             3: 180, //'bottom-right',
31545 //            4: 'bottom-left',
31546 //            5: 'left-top',
31547             6: 90, //'right-top',
31548 //            7: 'right-bottom',
31549             8: 270 //'left-bottom'
31550     },
31551     
31552     exifTagTypes : {
31553         // byte, 8-bit unsigned int:
31554         1: {
31555             getValue: function (dataView, dataOffset) {
31556                 return dataView.getUint8(dataOffset);
31557             },
31558             size: 1
31559         },
31560         // ascii, 8-bit byte:
31561         2: {
31562             getValue: function (dataView, dataOffset) {
31563                 return String.fromCharCode(dataView.getUint8(dataOffset));
31564             },
31565             size: 1,
31566             ascii: true
31567         },
31568         // short, 16 bit int:
31569         3: {
31570             getValue: function (dataView, dataOffset, littleEndian) {
31571                 return dataView.getUint16(dataOffset, littleEndian);
31572             },
31573             size: 2
31574         },
31575         // long, 32 bit int:
31576         4: {
31577             getValue: function (dataView, dataOffset, littleEndian) {
31578                 return dataView.getUint32(dataOffset, littleEndian);
31579             },
31580             size: 4
31581         },
31582         // rational = two long values, first is numerator, second is denominator:
31583         5: {
31584             getValue: function (dataView, dataOffset, littleEndian) {
31585                 return dataView.getUint32(dataOffset, littleEndian) /
31586                     dataView.getUint32(dataOffset + 4, littleEndian);
31587             },
31588             size: 8
31589         },
31590         // slong, 32 bit signed int:
31591         9: {
31592             getValue: function (dataView, dataOffset, littleEndian) {
31593                 return dataView.getInt32(dataOffset, littleEndian);
31594             },
31595             size: 4
31596         },
31597         // srational, two slongs, first is numerator, second is denominator:
31598         10: {
31599             getValue: function (dataView, dataOffset, littleEndian) {
31600                 return dataView.getInt32(dataOffset, littleEndian) /
31601                     dataView.getInt32(dataOffset + 4, littleEndian);
31602             },
31603             size: 8
31604         }
31605     },
31606     
31607     footer : {
31608         STANDARD : [
31609             {
31610                 tag : 'div',
31611                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31612                 action : 'rotate-left',
31613                 cn : [
31614                     {
31615                         tag : 'button',
31616                         cls : 'btn btn-default',
31617                         html : '<i class="fa fa-undo"></i>'
31618                     }
31619                 ]
31620             },
31621             {
31622                 tag : 'div',
31623                 cls : 'btn-group roo-upload-cropbox-picture',
31624                 action : 'picture',
31625                 cn : [
31626                     {
31627                         tag : 'button',
31628                         cls : 'btn btn-default',
31629                         html : '<i class="fa fa-picture-o"></i>'
31630                     }
31631                 ]
31632             },
31633             {
31634                 tag : 'div',
31635                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31636                 action : 'rotate-right',
31637                 cn : [
31638                     {
31639                         tag : 'button',
31640                         cls : 'btn btn-default',
31641                         html : '<i class="fa fa-repeat"></i>'
31642                     }
31643                 ]
31644             }
31645         ],
31646         DOCUMENT : [
31647             {
31648                 tag : 'div',
31649                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31650                 action : 'rotate-left',
31651                 cn : [
31652                     {
31653                         tag : 'button',
31654                         cls : 'btn btn-default',
31655                         html : '<i class="fa fa-undo"></i>'
31656                     }
31657                 ]
31658             },
31659             {
31660                 tag : 'div',
31661                 cls : 'btn-group roo-upload-cropbox-download',
31662                 action : 'download',
31663                 cn : [
31664                     {
31665                         tag : 'button',
31666                         cls : 'btn btn-default',
31667                         html : '<i class="fa fa-download"></i>'
31668                     }
31669                 ]
31670             },
31671             {
31672                 tag : 'div',
31673                 cls : 'btn-group roo-upload-cropbox-crop',
31674                 action : 'crop',
31675                 cn : [
31676                     {
31677                         tag : 'button',
31678                         cls : 'btn btn-default',
31679                         html : '<i class="fa fa-crop"></i>'
31680                     }
31681                 ]
31682             },
31683             {
31684                 tag : 'div',
31685                 cls : 'btn-group roo-upload-cropbox-trash',
31686                 action : 'trash',
31687                 cn : [
31688                     {
31689                         tag : 'button',
31690                         cls : 'btn btn-default',
31691                         html : '<i class="fa fa-trash"></i>'
31692                     }
31693                 ]
31694             },
31695             {
31696                 tag : 'div',
31697                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31698                 action : 'rotate-right',
31699                 cn : [
31700                     {
31701                         tag : 'button',
31702                         cls : 'btn btn-default',
31703                         html : '<i class="fa fa-repeat"></i>'
31704                     }
31705                 ]
31706             }
31707         ],
31708         ROTATOR : [
31709             {
31710                 tag : 'div',
31711                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31712                 action : 'rotate-left',
31713                 cn : [
31714                     {
31715                         tag : 'button',
31716                         cls : 'btn btn-default',
31717                         html : '<i class="fa fa-undo"></i>'
31718                     }
31719                 ]
31720             },
31721             {
31722                 tag : 'div',
31723                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31724                 action : 'rotate-right',
31725                 cn : [
31726                     {
31727                         tag : 'button',
31728                         cls : 'btn btn-default',
31729                         html : '<i class="fa fa-repeat"></i>'
31730                     }
31731                 ]
31732             }
31733         ]
31734     }
31735 });
31736
31737 /*
31738 * Licence: LGPL
31739 */
31740
31741 /**
31742  * @class Roo.bootstrap.DocumentManager
31743  * @extends Roo.bootstrap.Component
31744  * Bootstrap DocumentManager class
31745  * @cfg {String} paramName default 'imageUpload'
31746  * @cfg {String} toolTipName default 'filename'
31747  * @cfg {String} method default POST
31748  * @cfg {String} url action url
31749  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31750  * @cfg {Boolean} multiple multiple upload default true
31751  * @cfg {Number} thumbSize default 300
31752  * @cfg {String} fieldLabel
31753  * @cfg {Number} labelWidth default 4
31754  * @cfg {String} labelAlign (left|top) default left
31755  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31756 * @cfg {Number} labellg set the width of label (1-12)
31757  * @cfg {Number} labelmd set the width of label (1-12)
31758  * @cfg {Number} labelsm set the width of label (1-12)
31759  * @cfg {Number} labelxs set the width of label (1-12)
31760  * 
31761  * @constructor
31762  * Create a new DocumentManager
31763  * @param {Object} config The config object
31764  */
31765
31766 Roo.bootstrap.DocumentManager = function(config){
31767     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31768     
31769     this.files = [];
31770     this.delegates = [];
31771     
31772     this.addEvents({
31773         /**
31774          * @event initial
31775          * Fire when initial the DocumentManager
31776          * @param {Roo.bootstrap.DocumentManager} this
31777          */
31778         "initial" : true,
31779         /**
31780          * @event inspect
31781          * inspect selected file
31782          * @param {Roo.bootstrap.DocumentManager} this
31783          * @param {File} file
31784          */
31785         "inspect" : true,
31786         /**
31787          * @event exception
31788          * Fire when xhr load exception
31789          * @param {Roo.bootstrap.DocumentManager} this
31790          * @param {XMLHttpRequest} xhr
31791          */
31792         "exception" : true,
31793         /**
31794          * @event afterupload
31795          * Fire when xhr load exception
31796          * @param {Roo.bootstrap.DocumentManager} this
31797          * @param {XMLHttpRequest} xhr
31798          */
31799         "afterupload" : true,
31800         /**
31801          * @event prepare
31802          * prepare the form data
31803          * @param {Roo.bootstrap.DocumentManager} this
31804          * @param {Object} formData
31805          */
31806         "prepare" : true,
31807         /**
31808          * @event remove
31809          * Fire when remove the file
31810          * @param {Roo.bootstrap.DocumentManager} this
31811          * @param {Object} file
31812          */
31813         "remove" : true,
31814         /**
31815          * @event refresh
31816          * Fire after refresh the file
31817          * @param {Roo.bootstrap.DocumentManager} this
31818          */
31819         "refresh" : true,
31820         /**
31821          * @event click
31822          * Fire after click the image
31823          * @param {Roo.bootstrap.DocumentManager} this
31824          * @param {Object} file
31825          */
31826         "click" : true,
31827         /**
31828          * @event edit
31829          * Fire when upload a image and editable set to true
31830          * @param {Roo.bootstrap.DocumentManager} this
31831          * @param {Object} file
31832          */
31833         "edit" : true,
31834         /**
31835          * @event beforeselectfile
31836          * Fire before select file
31837          * @param {Roo.bootstrap.DocumentManager} this
31838          */
31839         "beforeselectfile" : true,
31840         /**
31841          * @event process
31842          * Fire before process file
31843          * @param {Roo.bootstrap.DocumentManager} this
31844          * @param {Object} file
31845          */
31846         "process" : true,
31847         /**
31848          * @event previewrendered
31849          * Fire when preview rendered
31850          * @param {Roo.bootstrap.DocumentManager} this
31851          * @param {Object} file
31852          */
31853         "previewrendered" : true,
31854         /**
31855          */
31856         "previewResize" : true
31857         
31858     });
31859 };
31860
31861 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31862     
31863     boxes : 0,
31864     inputName : '',
31865     thumbSize : 300,
31866     multiple : true,
31867     files : false,
31868     method : 'POST',
31869     url : '',
31870     paramName : 'imageUpload',
31871     toolTipName : 'filename',
31872     fieldLabel : '',
31873     labelWidth : 4,
31874     labelAlign : 'left',
31875     editable : true,
31876     delegates : false,
31877     xhr : false, 
31878     
31879     labellg : 0,
31880     labelmd : 0,
31881     labelsm : 0,
31882     labelxs : 0,
31883     
31884     getAutoCreate : function()
31885     {   
31886         var managerWidget = {
31887             tag : 'div',
31888             cls : 'roo-document-manager',
31889             cn : [
31890                 {
31891                     tag : 'input',
31892                     cls : 'roo-document-manager-selector',
31893                     type : 'file'
31894                 },
31895                 {
31896                     tag : 'div',
31897                     cls : 'roo-document-manager-uploader',
31898                     cn : [
31899                         {
31900                             tag : 'div',
31901                             cls : 'roo-document-manager-upload-btn',
31902                             html : '<i class="fa fa-plus"></i>'
31903                         }
31904                     ]
31905                     
31906                 }
31907             ]
31908         };
31909         
31910         var content = [
31911             {
31912                 tag : 'div',
31913                 cls : 'column col-md-12',
31914                 cn : managerWidget
31915             }
31916         ];
31917         
31918         if(this.fieldLabel.length){
31919             
31920             content = [
31921                 {
31922                     tag : 'div',
31923                     cls : 'column col-md-12',
31924                     html : this.fieldLabel
31925                 },
31926                 {
31927                     tag : 'div',
31928                     cls : 'column col-md-12',
31929                     cn : managerWidget
31930                 }
31931             ];
31932
31933             if(this.labelAlign == 'left'){
31934                 content = [
31935                     {
31936                         tag : 'div',
31937                         cls : 'column',
31938                         html : this.fieldLabel
31939                     },
31940                     {
31941                         tag : 'div',
31942                         cls : 'column',
31943                         cn : managerWidget
31944                     }
31945                 ];
31946                 
31947                 if(this.labelWidth > 12){
31948                     content[0].style = "width: " + this.labelWidth + 'px';
31949                 }
31950
31951                 if(this.labelWidth < 13 && this.labelmd == 0){
31952                     this.labelmd = this.labelWidth;
31953                 }
31954
31955                 if(this.labellg > 0){
31956                     content[0].cls += ' col-lg-' + this.labellg;
31957                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31958                 }
31959
31960                 if(this.labelmd > 0){
31961                     content[0].cls += ' col-md-' + this.labelmd;
31962                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31963                 }
31964
31965                 if(this.labelsm > 0){
31966                     content[0].cls += ' col-sm-' + this.labelsm;
31967                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31968                 }
31969
31970                 if(this.labelxs > 0){
31971                     content[0].cls += ' col-xs-' + this.labelxs;
31972                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31973                 }
31974                 
31975             }
31976         }
31977         
31978         var cfg = {
31979             tag : 'div',
31980             cls : 'row clearfix',
31981             cn : content
31982         };
31983         
31984         return cfg;
31985         
31986     },
31987     
31988     initEvents : function()
31989     {
31990         this.managerEl = this.el.select('.roo-document-manager', true).first();
31991         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31992         
31993         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31994         this.selectorEl.hide();
31995         
31996         if(this.multiple){
31997             this.selectorEl.attr('multiple', 'multiple');
31998         }
31999         
32000         this.selectorEl.on('change', this.onFileSelected, this);
32001         
32002         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32003         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32004         
32005         this.uploader.on('click', this.onUploaderClick, this);
32006         
32007         this.renderProgressDialog();
32008         
32009         var _this = this;
32010         
32011         window.addEventListener("resize", function() { _this.refresh(); } );
32012         
32013         this.fireEvent('initial', this);
32014     },
32015     
32016     renderProgressDialog : function()
32017     {
32018         var _this = this;
32019         
32020         this.progressDialog = new Roo.bootstrap.Modal({
32021             cls : 'roo-document-manager-progress-dialog',
32022             allow_close : false,
32023             animate : false,
32024             title : '',
32025             buttons : [
32026                 {
32027                     name  :'cancel',
32028                     weight : 'danger',
32029                     html : 'Cancel'
32030                 }
32031             ], 
32032             listeners : { 
32033                 btnclick : function() {
32034                     _this.uploadCancel();
32035                     this.hide();
32036                 }
32037             }
32038         });
32039          
32040         this.progressDialog.render(Roo.get(document.body));
32041          
32042         this.progress = new Roo.bootstrap.Progress({
32043             cls : 'roo-document-manager-progress',
32044             active : true,
32045             striped : true
32046         });
32047         
32048         this.progress.render(this.progressDialog.getChildContainer());
32049         
32050         this.progressBar = new Roo.bootstrap.ProgressBar({
32051             cls : 'roo-document-manager-progress-bar',
32052             aria_valuenow : 0,
32053             aria_valuemin : 0,
32054             aria_valuemax : 12,
32055             panel : 'success'
32056         });
32057         
32058         this.progressBar.render(this.progress.getChildContainer());
32059     },
32060     
32061     onUploaderClick : function(e)
32062     {
32063         e.preventDefault();
32064      
32065         if(this.fireEvent('beforeselectfile', this) != false){
32066             this.selectorEl.dom.click();
32067         }
32068         
32069     },
32070     
32071     onFileSelected : function(e)
32072     {
32073         e.preventDefault();
32074         
32075         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32076             return;
32077         }
32078         
32079         Roo.each(this.selectorEl.dom.files, function(file){
32080             if(this.fireEvent('inspect', this, file) != false){
32081                 this.files.push(file);
32082             }
32083         }, this);
32084         
32085         this.queue();
32086         
32087     },
32088     
32089     queue : function()
32090     {
32091         this.selectorEl.dom.value = '';
32092         
32093         if(!this.files || !this.files.length){
32094             return;
32095         }
32096         
32097         if(this.boxes > 0 && this.files.length > this.boxes){
32098             this.files = this.files.slice(0, this.boxes);
32099         }
32100         
32101         this.uploader.show();
32102         
32103         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32104             this.uploader.hide();
32105         }
32106         
32107         var _this = this;
32108         
32109         var files = [];
32110         
32111         var docs = [];
32112         
32113         Roo.each(this.files, function(file){
32114             
32115             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32116                 var f = this.renderPreview(file);
32117                 files.push(f);
32118                 return;
32119             }
32120             
32121             if(file.type.indexOf('image') != -1){
32122                 this.delegates.push(
32123                     (function(){
32124                         _this.process(file);
32125                     }).createDelegate(this)
32126                 );
32127         
32128                 return;
32129             }
32130             
32131             docs.push(
32132                 (function(){
32133                     _this.process(file);
32134                 }).createDelegate(this)
32135             );
32136             
32137         }, this);
32138         
32139         this.files = files;
32140         
32141         this.delegates = this.delegates.concat(docs);
32142         
32143         if(!this.delegates.length){
32144             this.refresh();
32145             return;
32146         }
32147         
32148         this.progressBar.aria_valuemax = this.delegates.length;
32149         
32150         this.arrange();
32151         
32152         return;
32153     },
32154     
32155     arrange : function()
32156     {
32157         if(!this.delegates.length){
32158             this.progressDialog.hide();
32159             this.refresh();
32160             return;
32161         }
32162         
32163         var delegate = this.delegates.shift();
32164         
32165         this.progressDialog.show();
32166         
32167         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32168         
32169         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32170         
32171         delegate();
32172     },
32173     
32174     refresh : function()
32175     {
32176         this.uploader.show();
32177         
32178         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32179             this.uploader.hide();
32180         }
32181         
32182         Roo.isTouch ? this.closable(false) : this.closable(true);
32183         
32184         this.fireEvent('refresh', this);
32185     },
32186     
32187     onRemove : function(e, el, o)
32188     {
32189         e.preventDefault();
32190         
32191         this.fireEvent('remove', this, o);
32192         
32193     },
32194     
32195     remove : function(o)
32196     {
32197         var files = [];
32198         
32199         Roo.each(this.files, function(file){
32200             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32201                 files.push(file);
32202                 return;
32203             }
32204
32205             o.target.remove();
32206
32207         }, this);
32208         
32209         this.files = files;
32210         
32211         this.refresh();
32212     },
32213     
32214     clear : function()
32215     {
32216         Roo.each(this.files, function(file){
32217             if(!file.target){
32218                 return;
32219             }
32220             
32221             file.target.remove();
32222
32223         }, this);
32224         
32225         this.files = [];
32226         
32227         this.refresh();
32228     },
32229     
32230     onClick : function(e, el, o)
32231     {
32232         e.preventDefault();
32233         
32234         this.fireEvent('click', this, o);
32235         
32236     },
32237     
32238     closable : function(closable)
32239     {
32240         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32241             
32242             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32243             
32244             if(closable){
32245                 el.show();
32246                 return;
32247             }
32248             
32249             el.hide();
32250             
32251         }, this);
32252     },
32253     
32254     xhrOnLoad : function(xhr)
32255     {
32256         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32257             el.remove();
32258         }, this);
32259         
32260         if (xhr.readyState !== 4) {
32261             this.arrange();
32262             this.fireEvent('exception', this, xhr);
32263             return;
32264         }
32265
32266         var response = Roo.decode(xhr.responseText);
32267         
32268         if(!response.success){
32269             this.arrange();
32270             this.fireEvent('exception', this, xhr);
32271             return;
32272         }
32273         
32274         var file = this.renderPreview(response.data);
32275         
32276         this.files.push(file);
32277         
32278         this.arrange();
32279         
32280         this.fireEvent('afterupload', this, xhr);
32281         
32282     },
32283     
32284     xhrOnError : function(xhr)
32285     {
32286         Roo.log('xhr on error');
32287         
32288         var response = Roo.decode(xhr.responseText);
32289           
32290         Roo.log(response);
32291         
32292         this.arrange();
32293     },
32294     
32295     process : function(file)
32296     {
32297         if(this.fireEvent('process', this, file) !== false){
32298             if(this.editable && file.type.indexOf('image') != -1){
32299                 this.fireEvent('edit', this, file);
32300                 return;
32301             }
32302
32303             this.uploadStart(file, false);
32304
32305             return;
32306         }
32307         
32308     },
32309     
32310     uploadStart : function(file, crop)
32311     {
32312         this.xhr = new XMLHttpRequest();
32313         
32314         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32315             this.arrange();
32316             return;
32317         }
32318         
32319         file.xhr = this.xhr;
32320             
32321         this.managerEl.createChild({
32322             tag : 'div',
32323             cls : 'roo-document-manager-loading',
32324             cn : [
32325                 {
32326                     tag : 'div',
32327                     tooltip : file.name,
32328                     cls : 'roo-document-manager-thumb',
32329                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32330                 }
32331             ]
32332
32333         });
32334
32335         this.xhr.open(this.method, this.url, true);
32336         
32337         var headers = {
32338             "Accept": "application/json",
32339             "Cache-Control": "no-cache",
32340             "X-Requested-With": "XMLHttpRequest"
32341         };
32342         
32343         for (var headerName in headers) {
32344             var headerValue = headers[headerName];
32345             if (headerValue) {
32346                 this.xhr.setRequestHeader(headerName, headerValue);
32347             }
32348         }
32349         
32350         var _this = this;
32351         
32352         this.xhr.onload = function()
32353         {
32354             _this.xhrOnLoad(_this.xhr);
32355         }
32356         
32357         this.xhr.onerror = function()
32358         {
32359             _this.xhrOnError(_this.xhr);
32360         }
32361         
32362         var formData = new FormData();
32363
32364         formData.append('returnHTML', 'NO');
32365         
32366         if(crop){
32367             formData.append('crop', crop);
32368         }
32369         
32370         formData.append(this.paramName, file, file.name);
32371         
32372         var options = {
32373             file : file, 
32374             manually : false
32375         };
32376         
32377         if(this.fireEvent('prepare', this, formData, options) != false){
32378             
32379             if(options.manually){
32380                 return;
32381             }
32382             
32383             this.xhr.send(formData);
32384             return;
32385         };
32386         
32387         this.uploadCancel();
32388     },
32389     
32390     uploadCancel : function()
32391     {
32392         if (this.xhr) {
32393             this.xhr.abort();
32394         }
32395         
32396         this.delegates = [];
32397         
32398         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32399             el.remove();
32400         }, this);
32401         
32402         this.arrange();
32403     },
32404     
32405     renderPreview : function(file)
32406     {
32407         if(typeof(file.target) != 'undefined' && file.target){
32408             return file;
32409         }
32410         
32411         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32412         
32413         var previewEl = this.managerEl.createChild({
32414             tag : 'div',
32415             cls : 'roo-document-manager-preview',
32416             cn : [
32417                 {
32418                     tag : 'div',
32419                     tooltip : file[this.toolTipName],
32420                     cls : 'roo-document-manager-thumb',
32421                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32422                 },
32423                 {
32424                     tag : 'button',
32425                     cls : 'close',
32426                     html : '<i class="fa fa-times-circle"></i>'
32427                 }
32428             ]
32429         });
32430
32431         var close = previewEl.select('button.close', true).first();
32432
32433         close.on('click', this.onRemove, this, file);
32434
32435         file.target = previewEl;
32436
32437         var image = previewEl.select('img', true).first();
32438         
32439         var _this = this;
32440         
32441         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32442         
32443         image.on('click', this.onClick, this, file);
32444         
32445         this.fireEvent('previewrendered', this, file);
32446         
32447         return file;
32448         
32449     },
32450     
32451     onPreviewLoad : function(file, image)
32452     {
32453         if(typeof(file.target) == 'undefined' || !file.target){
32454             return;
32455         }
32456         
32457         var width = image.dom.naturalWidth || image.dom.width;
32458         var height = image.dom.naturalHeight || image.dom.height;
32459         
32460         if(!this.previewResize) {
32461             return;
32462         }
32463         
32464         if(width > height){
32465             file.target.addClass('wide');
32466             return;
32467         }
32468         
32469         file.target.addClass('tall');
32470         return;
32471         
32472     },
32473     
32474     uploadFromSource : function(file, crop)
32475     {
32476         this.xhr = new XMLHttpRequest();
32477         
32478         this.managerEl.createChild({
32479             tag : 'div',
32480             cls : 'roo-document-manager-loading',
32481             cn : [
32482                 {
32483                     tag : 'div',
32484                     tooltip : file.name,
32485                     cls : 'roo-document-manager-thumb',
32486                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32487                 }
32488             ]
32489
32490         });
32491
32492         this.xhr.open(this.method, this.url, true);
32493         
32494         var headers = {
32495             "Accept": "application/json",
32496             "Cache-Control": "no-cache",
32497             "X-Requested-With": "XMLHttpRequest"
32498         };
32499         
32500         for (var headerName in headers) {
32501             var headerValue = headers[headerName];
32502             if (headerValue) {
32503                 this.xhr.setRequestHeader(headerName, headerValue);
32504             }
32505         }
32506         
32507         var _this = this;
32508         
32509         this.xhr.onload = function()
32510         {
32511             _this.xhrOnLoad(_this.xhr);
32512         }
32513         
32514         this.xhr.onerror = function()
32515         {
32516             _this.xhrOnError(_this.xhr);
32517         }
32518         
32519         var formData = new FormData();
32520
32521         formData.append('returnHTML', 'NO');
32522         
32523         formData.append('crop', crop);
32524         
32525         if(typeof(file.filename) != 'undefined'){
32526             formData.append('filename', file.filename);
32527         }
32528         
32529         if(typeof(file.mimetype) != 'undefined'){
32530             formData.append('mimetype', file.mimetype);
32531         }
32532         
32533         Roo.log(formData);
32534         
32535         if(this.fireEvent('prepare', this, formData) != false){
32536             this.xhr.send(formData);
32537         };
32538     }
32539 });
32540
32541 /*
32542 * Licence: LGPL
32543 */
32544
32545 /**
32546  * @class Roo.bootstrap.DocumentViewer
32547  * @extends Roo.bootstrap.Component
32548  * Bootstrap DocumentViewer class
32549  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32550  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32551  * 
32552  * @constructor
32553  * Create a new DocumentViewer
32554  * @param {Object} config The config object
32555  */
32556
32557 Roo.bootstrap.DocumentViewer = function(config){
32558     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32559     
32560     this.addEvents({
32561         /**
32562          * @event initial
32563          * Fire after initEvent
32564          * @param {Roo.bootstrap.DocumentViewer} this
32565          */
32566         "initial" : true,
32567         /**
32568          * @event click
32569          * Fire after click
32570          * @param {Roo.bootstrap.DocumentViewer} this
32571          */
32572         "click" : true,
32573         /**
32574          * @event download
32575          * Fire after download button
32576          * @param {Roo.bootstrap.DocumentViewer} this
32577          */
32578         "download" : true,
32579         /**
32580          * @event trash
32581          * Fire after trash button
32582          * @param {Roo.bootstrap.DocumentViewer} this
32583          */
32584         "trash" : true
32585         
32586     });
32587 };
32588
32589 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32590     
32591     showDownload : true,
32592     
32593     showTrash : true,
32594     
32595     getAutoCreate : function()
32596     {
32597         var cfg = {
32598             tag : 'div',
32599             cls : 'roo-document-viewer',
32600             cn : [
32601                 {
32602                     tag : 'div',
32603                     cls : 'roo-document-viewer-body',
32604                     cn : [
32605                         {
32606                             tag : 'div',
32607                             cls : 'roo-document-viewer-thumb',
32608                             cn : [
32609                                 {
32610                                     tag : 'img',
32611                                     cls : 'roo-document-viewer-image'
32612                                 }
32613                             ]
32614                         }
32615                     ]
32616                 },
32617                 {
32618                     tag : 'div',
32619                     cls : 'roo-document-viewer-footer',
32620                     cn : {
32621                         tag : 'div',
32622                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32623                         cn : [
32624                             {
32625                                 tag : 'div',
32626                                 cls : 'btn-group roo-document-viewer-download',
32627                                 cn : [
32628                                     {
32629                                         tag : 'button',
32630                                         cls : 'btn btn-default',
32631                                         html : '<i class="fa fa-download"></i>'
32632                                     }
32633                                 ]
32634                             },
32635                             {
32636                                 tag : 'div',
32637                                 cls : 'btn-group roo-document-viewer-trash',
32638                                 cn : [
32639                                     {
32640                                         tag : 'button',
32641                                         cls : 'btn btn-default',
32642                                         html : '<i class="fa fa-trash"></i>'
32643                                     }
32644                                 ]
32645                             }
32646                         ]
32647                     }
32648                 }
32649             ]
32650         };
32651         
32652         return cfg;
32653     },
32654     
32655     initEvents : function()
32656     {
32657         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32658         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32659         
32660         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32661         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32662         
32663         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32664         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32665         
32666         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32667         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32668         
32669         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32670         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32671         
32672         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32673         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32674         
32675         this.bodyEl.on('click', this.onClick, this);
32676         this.downloadBtn.on('click', this.onDownload, this);
32677         this.trashBtn.on('click', this.onTrash, this);
32678         
32679         this.downloadBtn.hide();
32680         this.trashBtn.hide();
32681         
32682         if(this.showDownload){
32683             this.downloadBtn.show();
32684         }
32685         
32686         if(this.showTrash){
32687             this.trashBtn.show();
32688         }
32689         
32690         if(!this.showDownload && !this.showTrash) {
32691             this.footerEl.hide();
32692         }
32693         
32694     },
32695     
32696     initial : function()
32697     {
32698         this.fireEvent('initial', this);
32699         
32700     },
32701     
32702     onClick : function(e)
32703     {
32704         e.preventDefault();
32705         
32706         this.fireEvent('click', this);
32707     },
32708     
32709     onDownload : function(e)
32710     {
32711         e.preventDefault();
32712         
32713         this.fireEvent('download', this);
32714     },
32715     
32716     onTrash : function(e)
32717     {
32718         e.preventDefault();
32719         
32720         this.fireEvent('trash', this);
32721     }
32722     
32723 });
32724 /*
32725  * - LGPL
32726  *
32727  * nav progress bar
32728  * 
32729  */
32730
32731 /**
32732  * @class Roo.bootstrap.NavProgressBar
32733  * @extends Roo.bootstrap.Component
32734  * Bootstrap NavProgressBar class
32735  * 
32736  * @constructor
32737  * Create a new nav progress bar
32738  * @param {Object} config The config object
32739  */
32740
32741 Roo.bootstrap.NavProgressBar = function(config){
32742     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32743
32744     this.bullets = this.bullets || [];
32745    
32746 //    Roo.bootstrap.NavProgressBar.register(this);
32747      this.addEvents({
32748         /**
32749              * @event changed
32750              * Fires when the active item changes
32751              * @param {Roo.bootstrap.NavProgressBar} this
32752              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32753              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32754          */
32755         'changed': true
32756      });
32757     
32758 };
32759
32760 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32761     
32762     bullets : [],
32763     barItems : [],
32764     
32765     getAutoCreate : function()
32766     {
32767         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32768         
32769         cfg = {
32770             tag : 'div',
32771             cls : 'roo-navigation-bar-group',
32772             cn : [
32773                 {
32774                     tag : 'div',
32775                     cls : 'roo-navigation-top-bar'
32776                 },
32777                 {
32778                     tag : 'div',
32779                     cls : 'roo-navigation-bullets-bar',
32780                     cn : [
32781                         {
32782                             tag : 'ul',
32783                             cls : 'roo-navigation-bar'
32784                         }
32785                     ]
32786                 },
32787                 
32788                 {
32789                     tag : 'div',
32790                     cls : 'roo-navigation-bottom-bar'
32791                 }
32792             ]
32793             
32794         };
32795         
32796         return cfg;
32797         
32798     },
32799     
32800     initEvents: function() 
32801     {
32802         
32803     },
32804     
32805     onRender : function(ct, position) 
32806     {
32807         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32808         
32809         if(this.bullets.length){
32810             Roo.each(this.bullets, function(b){
32811                this.addItem(b);
32812             }, this);
32813         }
32814         
32815         this.format();
32816         
32817     },
32818     
32819     addItem : function(cfg)
32820     {
32821         var item = new Roo.bootstrap.NavProgressItem(cfg);
32822         
32823         item.parentId = this.id;
32824         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32825         
32826         if(cfg.html){
32827             var top = new Roo.bootstrap.Element({
32828                 tag : 'div',
32829                 cls : 'roo-navigation-bar-text'
32830             });
32831             
32832             var bottom = new Roo.bootstrap.Element({
32833                 tag : 'div',
32834                 cls : 'roo-navigation-bar-text'
32835             });
32836             
32837             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32838             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32839             
32840             var topText = new Roo.bootstrap.Element({
32841                 tag : 'span',
32842                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32843             });
32844             
32845             var bottomText = new Roo.bootstrap.Element({
32846                 tag : 'span',
32847                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32848             });
32849             
32850             topText.onRender(top.el, null);
32851             bottomText.onRender(bottom.el, null);
32852             
32853             item.topEl = top;
32854             item.bottomEl = bottom;
32855         }
32856         
32857         this.barItems.push(item);
32858         
32859         return item;
32860     },
32861     
32862     getActive : function()
32863     {
32864         var active = false;
32865         
32866         Roo.each(this.barItems, function(v){
32867             
32868             if (!v.isActive()) {
32869                 return;
32870             }
32871             
32872             active = v;
32873             return false;
32874             
32875         });
32876         
32877         return active;
32878     },
32879     
32880     setActiveItem : function(item)
32881     {
32882         var prev = false;
32883         
32884         Roo.each(this.barItems, function(v){
32885             if (v.rid == item.rid) {
32886                 return ;
32887             }
32888             
32889             if (v.isActive()) {
32890                 v.setActive(false);
32891                 prev = v;
32892             }
32893         });
32894
32895         item.setActive(true);
32896         
32897         this.fireEvent('changed', this, item, prev);
32898     },
32899     
32900     getBarItem: function(rid)
32901     {
32902         var ret = false;
32903         
32904         Roo.each(this.barItems, function(e) {
32905             if (e.rid != rid) {
32906                 return;
32907             }
32908             
32909             ret =  e;
32910             return false;
32911         });
32912         
32913         return ret;
32914     },
32915     
32916     indexOfItem : function(item)
32917     {
32918         var index = false;
32919         
32920         Roo.each(this.barItems, function(v, i){
32921             
32922             if (v.rid != item.rid) {
32923                 return;
32924             }
32925             
32926             index = i;
32927             return false
32928         });
32929         
32930         return index;
32931     },
32932     
32933     setActiveNext : function()
32934     {
32935         var i = this.indexOfItem(this.getActive());
32936         
32937         if (i > this.barItems.length) {
32938             return;
32939         }
32940         
32941         this.setActiveItem(this.barItems[i+1]);
32942     },
32943     
32944     setActivePrev : function()
32945     {
32946         var i = this.indexOfItem(this.getActive());
32947         
32948         if (i  < 1) {
32949             return;
32950         }
32951         
32952         this.setActiveItem(this.barItems[i-1]);
32953     },
32954     
32955     format : function()
32956     {
32957         if(!this.barItems.length){
32958             return;
32959         }
32960      
32961         var width = 100 / this.barItems.length;
32962         
32963         Roo.each(this.barItems, function(i){
32964             i.el.setStyle('width', width + '%');
32965             i.topEl.el.setStyle('width', width + '%');
32966             i.bottomEl.el.setStyle('width', width + '%');
32967         }, this);
32968         
32969     }
32970     
32971 });
32972 /*
32973  * - LGPL
32974  *
32975  * Nav Progress Item
32976  * 
32977  */
32978
32979 /**
32980  * @class Roo.bootstrap.NavProgressItem
32981  * @extends Roo.bootstrap.Component
32982  * Bootstrap NavProgressItem class
32983  * @cfg {String} rid the reference id
32984  * @cfg {Boolean} active (true|false) Is item active default false
32985  * @cfg {Boolean} disabled (true|false) Is item active default false
32986  * @cfg {String} html
32987  * @cfg {String} position (top|bottom) text position default bottom
32988  * @cfg {String} icon show icon instead of number
32989  * 
32990  * @constructor
32991  * Create a new NavProgressItem
32992  * @param {Object} config The config object
32993  */
32994 Roo.bootstrap.NavProgressItem = function(config){
32995     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32996     this.addEvents({
32997         // raw events
32998         /**
32999          * @event click
33000          * The raw click event for the entire grid.
33001          * @param {Roo.bootstrap.NavProgressItem} this
33002          * @param {Roo.EventObject} e
33003          */
33004         "click" : true
33005     });
33006    
33007 };
33008
33009 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33010     
33011     rid : '',
33012     active : false,
33013     disabled : false,
33014     html : '',
33015     position : 'bottom',
33016     icon : false,
33017     
33018     getAutoCreate : function()
33019     {
33020         var iconCls = 'roo-navigation-bar-item-icon';
33021         
33022         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33023         
33024         var cfg = {
33025             tag: 'li',
33026             cls: 'roo-navigation-bar-item',
33027             cn : [
33028                 {
33029                     tag : 'i',
33030                     cls : iconCls
33031                 }
33032             ]
33033         };
33034         
33035         if(this.active){
33036             cfg.cls += ' active';
33037         }
33038         if(this.disabled){
33039             cfg.cls += ' disabled';
33040         }
33041         
33042         return cfg;
33043     },
33044     
33045     disable : function()
33046     {
33047         this.setDisabled(true);
33048     },
33049     
33050     enable : function()
33051     {
33052         this.setDisabled(false);
33053     },
33054     
33055     initEvents: function() 
33056     {
33057         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33058         
33059         this.iconEl.on('click', this.onClick, this);
33060     },
33061     
33062     onClick : function(e)
33063     {
33064         e.preventDefault();
33065         
33066         if(this.disabled){
33067             return;
33068         }
33069         
33070         if(this.fireEvent('click', this, e) === false){
33071             return;
33072         };
33073         
33074         this.parent().setActiveItem(this);
33075     },
33076     
33077     isActive: function () 
33078     {
33079         return this.active;
33080     },
33081     
33082     setActive : function(state)
33083     {
33084         if(this.active == state){
33085             return;
33086         }
33087         
33088         this.active = state;
33089         
33090         if (state) {
33091             this.el.addClass('active');
33092             return;
33093         }
33094         
33095         this.el.removeClass('active');
33096         
33097         return;
33098     },
33099     
33100     setDisabled : function(state)
33101     {
33102         if(this.disabled == state){
33103             return;
33104         }
33105         
33106         this.disabled = state;
33107         
33108         if (state) {
33109             this.el.addClass('disabled');
33110             return;
33111         }
33112         
33113         this.el.removeClass('disabled');
33114     },
33115     
33116     tooltipEl : function()
33117     {
33118         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33119     }
33120 });
33121  
33122
33123  /*
33124  * - LGPL
33125  *
33126  * FieldLabel
33127  * 
33128  */
33129
33130 /**
33131  * @class Roo.bootstrap.FieldLabel
33132  * @extends Roo.bootstrap.Component
33133  * Bootstrap FieldLabel class
33134  * @cfg {String} html contents of the element
33135  * @cfg {String} tag tag of the element default label
33136  * @cfg {String} cls class of the element
33137  * @cfg {String} target label target 
33138  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33139  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33140  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33141  * @cfg {String} iconTooltip default "This field is required"
33142  * @cfg {String} indicatorpos (left|right) default left
33143  * 
33144  * @constructor
33145  * Create a new FieldLabel
33146  * @param {Object} config The config object
33147  */
33148
33149 Roo.bootstrap.FieldLabel = function(config){
33150     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33151     
33152     this.addEvents({
33153             /**
33154              * @event invalid
33155              * Fires after the field has been marked as invalid.
33156              * @param {Roo.form.FieldLabel} this
33157              * @param {String} msg The validation message
33158              */
33159             invalid : true,
33160             /**
33161              * @event valid
33162              * Fires after the field has been validated with no errors.
33163              * @param {Roo.form.FieldLabel} this
33164              */
33165             valid : true
33166         });
33167 };
33168
33169 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33170     
33171     tag: 'label',
33172     cls: '',
33173     html: '',
33174     target: '',
33175     allowBlank : true,
33176     invalidClass : 'has-warning',
33177     validClass : 'has-success',
33178     iconTooltip : 'This field is required',
33179     indicatorpos : 'left',
33180     
33181     getAutoCreate : function(){
33182         
33183         var cls = "";
33184         if (!this.allowBlank) {
33185             cls  = "visible";
33186         }
33187         
33188         var cfg = {
33189             tag : this.tag,
33190             cls : 'roo-bootstrap-field-label ' + this.cls,
33191             for : this.target,
33192             cn : [
33193                 {
33194                     tag : 'i',
33195                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33196                     tooltip : this.iconTooltip
33197                 },
33198                 {
33199                     tag : 'span',
33200                     html : this.html
33201                 }
33202             ] 
33203         };
33204         
33205         if(this.indicatorpos == 'right'){
33206             var cfg = {
33207                 tag : this.tag,
33208                 cls : 'roo-bootstrap-field-label ' + this.cls,
33209                 for : this.target,
33210                 cn : [
33211                     {
33212                         tag : 'span',
33213                         html : this.html
33214                     },
33215                     {
33216                         tag : 'i',
33217                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33218                         tooltip : this.iconTooltip
33219                     }
33220                 ] 
33221             };
33222         }
33223         
33224         return cfg;
33225     },
33226     
33227     initEvents: function() 
33228     {
33229         Roo.bootstrap.Element.superclass.initEvents.call(this);
33230         
33231         this.indicator = this.indicatorEl();
33232         
33233         if(this.indicator){
33234             this.indicator.removeClass('visible');
33235             this.indicator.addClass('invisible');
33236         }
33237         
33238         Roo.bootstrap.FieldLabel.register(this);
33239     },
33240     
33241     indicatorEl : function()
33242     {
33243         var indicator = this.el.select('i.roo-required-indicator',true).first();
33244         
33245         if(!indicator){
33246             return false;
33247         }
33248         
33249         return indicator;
33250         
33251     },
33252     
33253     /**
33254      * Mark this field as valid
33255      */
33256     markValid : function()
33257     {
33258         if(this.indicator){
33259             this.indicator.removeClass('visible');
33260             this.indicator.addClass('invisible');
33261         }
33262         if (Roo.bootstrap.version == 3) {
33263             this.el.removeClass(this.invalidClass);
33264             this.el.addClass(this.validClass);
33265         } else {
33266             this.el.removeClass('is-invalid');
33267             this.el.addClass('is-valid');
33268         }
33269         
33270         
33271         this.fireEvent('valid', this);
33272     },
33273     
33274     /**
33275      * Mark this field as invalid
33276      * @param {String} msg The validation message
33277      */
33278     markInvalid : function(msg)
33279     {
33280         if(this.indicator){
33281             this.indicator.removeClass('invisible');
33282             this.indicator.addClass('visible');
33283         }
33284           if (Roo.bootstrap.version == 3) {
33285             this.el.removeClass(this.validClass);
33286             this.el.addClass(this.invalidClass);
33287         } else {
33288             this.el.removeClass('is-valid');
33289             this.el.addClass('is-invalid');
33290         }
33291         
33292         
33293         this.fireEvent('invalid', this, msg);
33294     }
33295     
33296    
33297 });
33298
33299 Roo.apply(Roo.bootstrap.FieldLabel, {
33300     
33301     groups: {},
33302     
33303      /**
33304     * register a FieldLabel Group
33305     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33306     */
33307     register : function(label)
33308     {
33309         if(this.groups.hasOwnProperty(label.target)){
33310             return;
33311         }
33312      
33313         this.groups[label.target] = label;
33314         
33315     },
33316     /**
33317     * fetch a FieldLabel Group based on the target
33318     * @param {string} target
33319     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33320     */
33321     get: function(target) {
33322         if (typeof(this.groups[target]) == 'undefined') {
33323             return false;
33324         }
33325         
33326         return this.groups[target] ;
33327     }
33328 });
33329
33330  
33331
33332  /*
33333  * - LGPL
33334  *
33335  * page DateSplitField.
33336  * 
33337  */
33338
33339
33340 /**
33341  * @class Roo.bootstrap.DateSplitField
33342  * @extends Roo.bootstrap.Component
33343  * Bootstrap DateSplitField class
33344  * @cfg {string} fieldLabel - the label associated
33345  * @cfg {Number} labelWidth set the width of label (0-12)
33346  * @cfg {String} labelAlign (top|left)
33347  * @cfg {Boolean} dayAllowBlank (true|false) default false
33348  * @cfg {Boolean} monthAllowBlank (true|false) default false
33349  * @cfg {Boolean} yearAllowBlank (true|false) default false
33350  * @cfg {string} dayPlaceholder 
33351  * @cfg {string} monthPlaceholder
33352  * @cfg {string} yearPlaceholder
33353  * @cfg {string} dayFormat default 'd'
33354  * @cfg {string} monthFormat default 'm'
33355  * @cfg {string} yearFormat default 'Y'
33356  * @cfg {Number} labellg set the width of label (1-12)
33357  * @cfg {Number} labelmd set the width of label (1-12)
33358  * @cfg {Number} labelsm set the width of label (1-12)
33359  * @cfg {Number} labelxs set the width of label (1-12)
33360
33361  *     
33362  * @constructor
33363  * Create a new DateSplitField
33364  * @param {Object} config The config object
33365  */
33366
33367 Roo.bootstrap.DateSplitField = function(config){
33368     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33369     
33370     this.addEvents({
33371         // raw events
33372          /**
33373          * @event years
33374          * getting the data of years
33375          * @param {Roo.bootstrap.DateSplitField} this
33376          * @param {Object} years
33377          */
33378         "years" : true,
33379         /**
33380          * @event days
33381          * getting the data of days
33382          * @param {Roo.bootstrap.DateSplitField} this
33383          * @param {Object} days
33384          */
33385         "days" : true,
33386         /**
33387          * @event invalid
33388          * Fires after the field has been marked as invalid.
33389          * @param {Roo.form.Field} this
33390          * @param {String} msg The validation message
33391          */
33392         invalid : true,
33393        /**
33394          * @event valid
33395          * Fires after the field has been validated with no errors.
33396          * @param {Roo.form.Field} this
33397          */
33398         valid : true
33399     });
33400 };
33401
33402 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33403     
33404     fieldLabel : '',
33405     labelAlign : 'top',
33406     labelWidth : 3,
33407     dayAllowBlank : false,
33408     monthAllowBlank : false,
33409     yearAllowBlank : false,
33410     dayPlaceholder : '',
33411     monthPlaceholder : '',
33412     yearPlaceholder : '',
33413     dayFormat : 'd',
33414     monthFormat : 'm',
33415     yearFormat : 'Y',
33416     isFormField : true,
33417     labellg : 0,
33418     labelmd : 0,
33419     labelsm : 0,
33420     labelxs : 0,
33421     
33422     getAutoCreate : function()
33423     {
33424         var cfg = {
33425             tag : 'div',
33426             cls : 'row roo-date-split-field-group',
33427             cn : [
33428                 {
33429                     tag : 'input',
33430                     type : 'hidden',
33431                     cls : 'form-hidden-field roo-date-split-field-group-value',
33432                     name : this.name
33433                 }
33434             ]
33435         };
33436         
33437         var labelCls = 'col-md-12';
33438         var contentCls = 'col-md-4';
33439         
33440         if(this.fieldLabel){
33441             
33442             var label = {
33443                 tag : 'div',
33444                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33445                 cn : [
33446                     {
33447                         tag : 'label',
33448                         html : this.fieldLabel
33449                     }
33450                 ]
33451             };
33452             
33453             if(this.labelAlign == 'left'){
33454             
33455                 if(this.labelWidth > 12){
33456                     label.style = "width: " + this.labelWidth + 'px';
33457                 }
33458
33459                 if(this.labelWidth < 13 && this.labelmd == 0){
33460                     this.labelmd = this.labelWidth;
33461                 }
33462
33463                 if(this.labellg > 0){
33464                     labelCls = ' col-lg-' + this.labellg;
33465                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33466                 }
33467
33468                 if(this.labelmd > 0){
33469                     labelCls = ' col-md-' + this.labelmd;
33470                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33471                 }
33472
33473                 if(this.labelsm > 0){
33474                     labelCls = ' col-sm-' + this.labelsm;
33475                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33476                 }
33477
33478                 if(this.labelxs > 0){
33479                     labelCls = ' col-xs-' + this.labelxs;
33480                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33481                 }
33482             }
33483             
33484             label.cls += ' ' + labelCls;
33485             
33486             cfg.cn.push(label);
33487         }
33488         
33489         Roo.each(['day', 'month', 'year'], function(t){
33490             cfg.cn.push({
33491                 tag : 'div',
33492                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33493             });
33494         }, this);
33495         
33496         return cfg;
33497     },
33498     
33499     inputEl: function ()
33500     {
33501         return this.el.select('.roo-date-split-field-group-value', true).first();
33502     },
33503     
33504     onRender : function(ct, position) 
33505     {
33506         var _this = this;
33507         
33508         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33509         
33510         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33511         
33512         this.dayField = new Roo.bootstrap.ComboBox({
33513             allowBlank : this.dayAllowBlank,
33514             alwaysQuery : true,
33515             displayField : 'value',
33516             editable : false,
33517             fieldLabel : '',
33518             forceSelection : true,
33519             mode : 'local',
33520             placeholder : this.dayPlaceholder,
33521             selectOnFocus : true,
33522             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33523             triggerAction : 'all',
33524             typeAhead : true,
33525             valueField : 'value',
33526             store : new Roo.data.SimpleStore({
33527                 data : (function() {    
33528                     var days = [];
33529                     _this.fireEvent('days', _this, days);
33530                     return days;
33531                 })(),
33532                 fields : [ 'value' ]
33533             }),
33534             listeners : {
33535                 select : function (_self, record, index)
33536                 {
33537                     _this.setValue(_this.getValue());
33538                 }
33539             }
33540         });
33541
33542         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33543         
33544         this.monthField = new Roo.bootstrap.MonthField({
33545             after : '<i class=\"fa fa-calendar\"></i>',
33546             allowBlank : this.monthAllowBlank,
33547             placeholder : this.monthPlaceholder,
33548             readOnly : true,
33549             listeners : {
33550                 render : function (_self)
33551                 {
33552                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33553                         e.preventDefault();
33554                         _self.focus();
33555                     });
33556                 },
33557                 select : function (_self, oldvalue, newvalue)
33558                 {
33559                     _this.setValue(_this.getValue());
33560                 }
33561             }
33562         });
33563         
33564         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33565         
33566         this.yearField = new Roo.bootstrap.ComboBox({
33567             allowBlank : this.yearAllowBlank,
33568             alwaysQuery : true,
33569             displayField : 'value',
33570             editable : false,
33571             fieldLabel : '',
33572             forceSelection : true,
33573             mode : 'local',
33574             placeholder : this.yearPlaceholder,
33575             selectOnFocus : true,
33576             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33577             triggerAction : 'all',
33578             typeAhead : true,
33579             valueField : 'value',
33580             store : new Roo.data.SimpleStore({
33581                 data : (function() {
33582                     var years = [];
33583                     _this.fireEvent('years', _this, years);
33584                     return years;
33585                 })(),
33586                 fields : [ 'value' ]
33587             }),
33588             listeners : {
33589                 select : function (_self, record, index)
33590                 {
33591                     _this.setValue(_this.getValue());
33592                 }
33593             }
33594         });
33595
33596         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33597     },
33598     
33599     setValue : function(v, format)
33600     {
33601         this.inputEl.dom.value = v;
33602         
33603         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33604         
33605         var d = Date.parseDate(v, f);
33606         
33607         if(!d){
33608             this.validate();
33609             return;
33610         }
33611         
33612         this.setDay(d.format(this.dayFormat));
33613         this.setMonth(d.format(this.monthFormat));
33614         this.setYear(d.format(this.yearFormat));
33615         
33616         this.validate();
33617         
33618         return;
33619     },
33620     
33621     setDay : function(v)
33622     {
33623         this.dayField.setValue(v);
33624         this.inputEl.dom.value = this.getValue();
33625         this.validate();
33626         return;
33627     },
33628     
33629     setMonth : function(v)
33630     {
33631         this.monthField.setValue(v, true);
33632         this.inputEl.dom.value = this.getValue();
33633         this.validate();
33634         return;
33635     },
33636     
33637     setYear : function(v)
33638     {
33639         this.yearField.setValue(v);
33640         this.inputEl.dom.value = this.getValue();
33641         this.validate();
33642         return;
33643     },
33644     
33645     getDay : function()
33646     {
33647         return this.dayField.getValue();
33648     },
33649     
33650     getMonth : function()
33651     {
33652         return this.monthField.getValue();
33653     },
33654     
33655     getYear : function()
33656     {
33657         return this.yearField.getValue();
33658     },
33659     
33660     getValue : function()
33661     {
33662         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33663         
33664         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33665         
33666         return date;
33667     },
33668     
33669     reset : function()
33670     {
33671         this.setDay('');
33672         this.setMonth('');
33673         this.setYear('');
33674         this.inputEl.dom.value = '';
33675         this.validate();
33676         return;
33677     },
33678     
33679     validate : function()
33680     {
33681         var d = this.dayField.validate();
33682         var m = this.monthField.validate();
33683         var y = this.yearField.validate();
33684         
33685         var valid = true;
33686         
33687         if(
33688                 (!this.dayAllowBlank && !d) ||
33689                 (!this.monthAllowBlank && !m) ||
33690                 (!this.yearAllowBlank && !y)
33691         ){
33692             valid = false;
33693         }
33694         
33695         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33696             return valid;
33697         }
33698         
33699         if(valid){
33700             this.markValid();
33701             return valid;
33702         }
33703         
33704         this.markInvalid();
33705         
33706         return valid;
33707     },
33708     
33709     markValid : function()
33710     {
33711         
33712         var label = this.el.select('label', true).first();
33713         var icon = this.el.select('i.fa-star', true).first();
33714
33715         if(label && icon){
33716             icon.remove();
33717         }
33718         
33719         this.fireEvent('valid', this);
33720     },
33721     
33722      /**
33723      * Mark this field as invalid
33724      * @param {String} msg The validation message
33725      */
33726     markInvalid : function(msg)
33727     {
33728         
33729         var label = this.el.select('label', true).first();
33730         var icon = this.el.select('i.fa-star', true).first();
33731
33732         if(label && !icon){
33733             this.el.select('.roo-date-split-field-label', true).createChild({
33734                 tag : 'i',
33735                 cls : 'text-danger fa fa-lg fa-star',
33736                 tooltip : 'This field is required',
33737                 style : 'margin-right:5px;'
33738             }, label, true);
33739         }
33740         
33741         this.fireEvent('invalid', this, msg);
33742     },
33743     
33744     clearInvalid : function()
33745     {
33746         var label = this.el.select('label', true).first();
33747         var icon = this.el.select('i.fa-star', true).first();
33748
33749         if(label && icon){
33750             icon.remove();
33751         }
33752         
33753         this.fireEvent('valid', this);
33754     },
33755     
33756     getName: function()
33757     {
33758         return this.name;
33759     }
33760     
33761 });
33762
33763  /**
33764  *
33765  * This is based on 
33766  * http://masonry.desandro.com
33767  *
33768  * The idea is to render all the bricks based on vertical width...
33769  *
33770  * The original code extends 'outlayer' - we might need to use that....
33771  * 
33772  */
33773
33774
33775 /**
33776  * @class Roo.bootstrap.LayoutMasonry
33777  * @extends Roo.bootstrap.Component
33778  * Bootstrap Layout Masonry class
33779  * 
33780  * @constructor
33781  * Create a new Element
33782  * @param {Object} config The config object
33783  */
33784
33785 Roo.bootstrap.LayoutMasonry = function(config){
33786     
33787     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33788     
33789     this.bricks = [];
33790     
33791     Roo.bootstrap.LayoutMasonry.register(this);
33792     
33793     this.addEvents({
33794         // raw events
33795         /**
33796          * @event layout
33797          * Fire after layout the items
33798          * @param {Roo.bootstrap.LayoutMasonry} this
33799          * @param {Roo.EventObject} e
33800          */
33801         "layout" : true
33802     });
33803     
33804 };
33805
33806 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33807     
33808     /**
33809      * @cfg {Boolean} isLayoutInstant = no animation?
33810      */   
33811     isLayoutInstant : false, // needed?
33812    
33813     /**
33814      * @cfg {Number} boxWidth  width of the columns
33815      */   
33816     boxWidth : 450,
33817     
33818       /**
33819      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33820      */   
33821     boxHeight : 0,
33822     
33823     /**
33824      * @cfg {Number} padWidth padding below box..
33825      */   
33826     padWidth : 10, 
33827     
33828     /**
33829      * @cfg {Number} gutter gutter width..
33830      */   
33831     gutter : 10,
33832     
33833      /**
33834      * @cfg {Number} maxCols maximum number of columns
33835      */   
33836     
33837     maxCols: 0,
33838     
33839     /**
33840      * @cfg {Boolean} isAutoInitial defalut true
33841      */   
33842     isAutoInitial : true, 
33843     
33844     containerWidth: 0,
33845     
33846     /**
33847      * @cfg {Boolean} isHorizontal defalut false
33848      */   
33849     isHorizontal : false, 
33850
33851     currentSize : null,
33852     
33853     tag: 'div',
33854     
33855     cls: '',
33856     
33857     bricks: null, //CompositeElement
33858     
33859     cols : 1,
33860     
33861     _isLayoutInited : false,
33862     
33863 //    isAlternative : false, // only use for vertical layout...
33864     
33865     /**
33866      * @cfg {Number} alternativePadWidth padding below box..
33867      */   
33868     alternativePadWidth : 50,
33869     
33870     selectedBrick : [],
33871     
33872     getAutoCreate : function(){
33873         
33874         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33875         
33876         var cfg = {
33877             tag: this.tag,
33878             cls: 'blog-masonary-wrapper ' + this.cls,
33879             cn : {
33880                 cls : 'mas-boxes masonary'
33881             }
33882         };
33883         
33884         return cfg;
33885     },
33886     
33887     getChildContainer: function( )
33888     {
33889         if (this.boxesEl) {
33890             return this.boxesEl;
33891         }
33892         
33893         this.boxesEl = this.el.select('.mas-boxes').first();
33894         
33895         return this.boxesEl;
33896     },
33897     
33898     
33899     initEvents : function()
33900     {
33901         var _this = this;
33902         
33903         if(this.isAutoInitial){
33904             Roo.log('hook children rendered');
33905             this.on('childrenrendered', function() {
33906                 Roo.log('children rendered');
33907                 _this.initial();
33908             } ,this);
33909         }
33910     },
33911     
33912     initial : function()
33913     {
33914         this.selectedBrick = [];
33915         
33916         this.currentSize = this.el.getBox(true);
33917         
33918         Roo.EventManager.onWindowResize(this.resize, this); 
33919
33920         if(!this.isAutoInitial){
33921             this.layout();
33922             return;
33923         }
33924         
33925         this.layout();
33926         
33927         return;
33928         //this.layout.defer(500,this);
33929         
33930     },
33931     
33932     resize : function()
33933     {
33934         var cs = this.el.getBox(true);
33935         
33936         if (
33937                 this.currentSize.width == cs.width && 
33938                 this.currentSize.x == cs.x && 
33939                 this.currentSize.height == cs.height && 
33940                 this.currentSize.y == cs.y 
33941         ) {
33942             Roo.log("no change in with or X or Y");
33943             return;
33944         }
33945         
33946         this.currentSize = cs;
33947         
33948         this.layout();
33949         
33950     },
33951     
33952     layout : function()
33953     {   
33954         this._resetLayout();
33955         
33956         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33957         
33958         this.layoutItems( isInstant );
33959       
33960         this._isLayoutInited = true;
33961         
33962         this.fireEvent('layout', this);
33963         
33964     },
33965     
33966     _resetLayout : function()
33967     {
33968         if(this.isHorizontal){
33969             this.horizontalMeasureColumns();
33970             return;
33971         }
33972         
33973         this.verticalMeasureColumns();
33974         
33975     },
33976     
33977     verticalMeasureColumns : function()
33978     {
33979         this.getContainerWidth();
33980         
33981 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33982 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33983 //            return;
33984 //        }
33985         
33986         var boxWidth = this.boxWidth + this.padWidth;
33987         
33988         if(this.containerWidth < this.boxWidth){
33989             boxWidth = this.containerWidth
33990         }
33991         
33992         var containerWidth = this.containerWidth;
33993         
33994         var cols = Math.floor(containerWidth / boxWidth);
33995         
33996         this.cols = Math.max( cols, 1 );
33997         
33998         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33999         
34000         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34001         
34002         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34003         
34004         this.colWidth = boxWidth + avail - this.padWidth;
34005         
34006         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34007         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34008     },
34009     
34010     horizontalMeasureColumns : function()
34011     {
34012         this.getContainerWidth();
34013         
34014         var boxWidth = this.boxWidth;
34015         
34016         if(this.containerWidth < boxWidth){
34017             boxWidth = this.containerWidth;
34018         }
34019         
34020         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34021         
34022         this.el.setHeight(boxWidth);
34023         
34024     },
34025     
34026     getContainerWidth : function()
34027     {
34028         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34029     },
34030     
34031     layoutItems : function( isInstant )
34032     {
34033         Roo.log(this.bricks);
34034         
34035         var items = Roo.apply([], this.bricks);
34036         
34037         if(this.isHorizontal){
34038             this._horizontalLayoutItems( items , isInstant );
34039             return;
34040         }
34041         
34042 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34043 //            this._verticalAlternativeLayoutItems( items , isInstant );
34044 //            return;
34045 //        }
34046         
34047         this._verticalLayoutItems( items , isInstant );
34048         
34049     },
34050     
34051     _verticalLayoutItems : function ( items , isInstant)
34052     {
34053         if ( !items || !items.length ) {
34054             return;
34055         }
34056         
34057         var standard = [
34058             ['xs', 'xs', 'xs', 'tall'],
34059             ['xs', 'xs', 'tall'],
34060             ['xs', 'xs', 'sm'],
34061             ['xs', 'xs', 'xs'],
34062             ['xs', 'tall'],
34063             ['xs', 'sm'],
34064             ['xs', 'xs'],
34065             ['xs'],
34066             
34067             ['sm', 'xs', 'xs'],
34068             ['sm', 'xs'],
34069             ['sm'],
34070             
34071             ['tall', 'xs', 'xs', 'xs'],
34072             ['tall', 'xs', 'xs'],
34073             ['tall', 'xs'],
34074             ['tall']
34075             
34076         ];
34077         
34078         var queue = [];
34079         
34080         var boxes = [];
34081         
34082         var box = [];
34083         
34084         Roo.each(items, function(item, k){
34085             
34086             switch (item.size) {
34087                 // these layouts take up a full box,
34088                 case 'md' :
34089                 case 'md-left' :
34090                 case 'md-right' :
34091                 case 'wide' :
34092                     
34093                     if(box.length){
34094                         boxes.push(box);
34095                         box = [];
34096                     }
34097                     
34098                     boxes.push([item]);
34099                     
34100                     break;
34101                     
34102                 case 'xs' :
34103                 case 'sm' :
34104                 case 'tall' :
34105                     
34106                     box.push(item);
34107                     
34108                     break;
34109                 default :
34110                     break;
34111                     
34112             }
34113             
34114         }, this);
34115         
34116         if(box.length){
34117             boxes.push(box);
34118             box = [];
34119         }
34120         
34121         var filterPattern = function(box, length)
34122         {
34123             if(!box.length){
34124                 return;
34125             }
34126             
34127             var match = false;
34128             
34129             var pattern = box.slice(0, length);
34130             
34131             var format = [];
34132             
34133             Roo.each(pattern, function(i){
34134                 format.push(i.size);
34135             }, this);
34136             
34137             Roo.each(standard, function(s){
34138                 
34139                 if(String(s) != String(format)){
34140                     return;
34141                 }
34142                 
34143                 match = true;
34144                 return false;
34145                 
34146             }, this);
34147             
34148             if(!match && length == 1){
34149                 return;
34150             }
34151             
34152             if(!match){
34153                 filterPattern(box, length - 1);
34154                 return;
34155             }
34156                 
34157             queue.push(pattern);
34158
34159             box = box.slice(length, box.length);
34160
34161             filterPattern(box, 4);
34162
34163             return;
34164             
34165         }
34166         
34167         Roo.each(boxes, function(box, k){
34168             
34169             if(!box.length){
34170                 return;
34171             }
34172             
34173             if(box.length == 1){
34174                 queue.push(box);
34175                 return;
34176             }
34177             
34178             filterPattern(box, 4);
34179             
34180         }, this);
34181         
34182         this._processVerticalLayoutQueue( queue, isInstant );
34183         
34184     },
34185     
34186 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34187 //    {
34188 //        if ( !items || !items.length ) {
34189 //            return;
34190 //        }
34191 //
34192 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34193 //        
34194 //    },
34195     
34196     _horizontalLayoutItems : function ( items , isInstant)
34197     {
34198         if ( !items || !items.length || items.length < 3) {
34199             return;
34200         }
34201         
34202         items.reverse();
34203         
34204         var eItems = items.slice(0, 3);
34205         
34206         items = items.slice(3, items.length);
34207         
34208         var standard = [
34209             ['xs', 'xs', 'xs', 'wide'],
34210             ['xs', 'xs', 'wide'],
34211             ['xs', 'xs', 'sm'],
34212             ['xs', 'xs', 'xs'],
34213             ['xs', 'wide'],
34214             ['xs', 'sm'],
34215             ['xs', 'xs'],
34216             ['xs'],
34217             
34218             ['sm', 'xs', 'xs'],
34219             ['sm', 'xs'],
34220             ['sm'],
34221             
34222             ['wide', 'xs', 'xs', 'xs'],
34223             ['wide', 'xs', 'xs'],
34224             ['wide', 'xs'],
34225             ['wide'],
34226             
34227             ['wide-thin']
34228         ];
34229         
34230         var queue = [];
34231         
34232         var boxes = [];
34233         
34234         var box = [];
34235         
34236         Roo.each(items, function(item, k){
34237             
34238             switch (item.size) {
34239                 case 'md' :
34240                 case 'md-left' :
34241                 case 'md-right' :
34242                 case 'tall' :
34243                     
34244                     if(box.length){
34245                         boxes.push(box);
34246                         box = [];
34247                     }
34248                     
34249                     boxes.push([item]);
34250                     
34251                     break;
34252                     
34253                 case 'xs' :
34254                 case 'sm' :
34255                 case 'wide' :
34256                 case 'wide-thin' :
34257                     
34258                     box.push(item);
34259                     
34260                     break;
34261                 default :
34262                     break;
34263                     
34264             }
34265             
34266         }, this);
34267         
34268         if(box.length){
34269             boxes.push(box);
34270             box = [];
34271         }
34272         
34273         var filterPattern = function(box, length)
34274         {
34275             if(!box.length){
34276                 return;
34277             }
34278             
34279             var match = false;
34280             
34281             var pattern = box.slice(0, length);
34282             
34283             var format = [];
34284             
34285             Roo.each(pattern, function(i){
34286                 format.push(i.size);
34287             }, this);
34288             
34289             Roo.each(standard, function(s){
34290                 
34291                 if(String(s) != String(format)){
34292                     return;
34293                 }
34294                 
34295                 match = true;
34296                 return false;
34297                 
34298             }, this);
34299             
34300             if(!match && length == 1){
34301                 return;
34302             }
34303             
34304             if(!match){
34305                 filterPattern(box, length - 1);
34306                 return;
34307             }
34308                 
34309             queue.push(pattern);
34310
34311             box = box.slice(length, box.length);
34312
34313             filterPattern(box, 4);
34314
34315             return;
34316             
34317         }
34318         
34319         Roo.each(boxes, function(box, k){
34320             
34321             if(!box.length){
34322                 return;
34323             }
34324             
34325             if(box.length == 1){
34326                 queue.push(box);
34327                 return;
34328             }
34329             
34330             filterPattern(box, 4);
34331             
34332         }, this);
34333         
34334         
34335         var prune = [];
34336         
34337         var pos = this.el.getBox(true);
34338         
34339         var minX = pos.x;
34340         
34341         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34342         
34343         var hit_end = false;
34344         
34345         Roo.each(queue, function(box){
34346             
34347             if(hit_end){
34348                 
34349                 Roo.each(box, function(b){
34350                 
34351                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34352                     b.el.hide();
34353
34354                 }, this);
34355
34356                 return;
34357             }
34358             
34359             var mx = 0;
34360             
34361             Roo.each(box, function(b){
34362                 
34363                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34364                 b.el.show();
34365
34366                 mx = Math.max(mx, b.x);
34367                 
34368             }, this);
34369             
34370             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34371             
34372             if(maxX < minX){
34373                 
34374                 Roo.each(box, function(b){
34375                 
34376                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34377                     b.el.hide();
34378                     
34379                 }, this);
34380                 
34381                 hit_end = true;
34382                 
34383                 return;
34384             }
34385             
34386             prune.push(box);
34387             
34388         }, this);
34389         
34390         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34391     },
34392     
34393     /** Sets position of item in DOM
34394     * @param {Element} item
34395     * @param {Number} x - horizontal position
34396     * @param {Number} y - vertical position
34397     * @param {Boolean} isInstant - disables transitions
34398     */
34399     _processVerticalLayoutQueue : function( queue, isInstant )
34400     {
34401         var pos = this.el.getBox(true);
34402         var x = pos.x;
34403         var y = pos.y;
34404         var maxY = [];
34405         
34406         for (var i = 0; i < this.cols; i++){
34407             maxY[i] = pos.y;
34408         }
34409         
34410         Roo.each(queue, function(box, k){
34411             
34412             var col = k % this.cols;
34413             
34414             Roo.each(box, function(b,kk){
34415                 
34416                 b.el.position('absolute');
34417                 
34418                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34419                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34420                 
34421                 if(b.size == 'md-left' || b.size == 'md-right'){
34422                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34423                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34424                 }
34425                 
34426                 b.el.setWidth(width);
34427                 b.el.setHeight(height);
34428                 // iframe?
34429                 b.el.select('iframe',true).setSize(width,height);
34430                 
34431             }, this);
34432             
34433             for (var i = 0; i < this.cols; i++){
34434                 
34435                 if(maxY[i] < maxY[col]){
34436                     col = i;
34437                     continue;
34438                 }
34439                 
34440                 col = Math.min(col, i);
34441                 
34442             }
34443             
34444             x = pos.x + col * (this.colWidth + this.padWidth);
34445             
34446             y = maxY[col];
34447             
34448             var positions = [];
34449             
34450             switch (box.length){
34451                 case 1 :
34452                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34453                     break;
34454                 case 2 :
34455                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34456                     break;
34457                 case 3 :
34458                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34459                     break;
34460                 case 4 :
34461                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34462                     break;
34463                 default :
34464                     break;
34465             }
34466             
34467             Roo.each(box, function(b,kk){
34468                 
34469                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34470                 
34471                 var sz = b.el.getSize();
34472                 
34473                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34474                 
34475             }, this);
34476             
34477         }, this);
34478         
34479         var mY = 0;
34480         
34481         for (var i = 0; i < this.cols; i++){
34482             mY = Math.max(mY, maxY[i]);
34483         }
34484         
34485         this.el.setHeight(mY - pos.y);
34486         
34487     },
34488     
34489 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34490 //    {
34491 //        var pos = this.el.getBox(true);
34492 //        var x = pos.x;
34493 //        var y = pos.y;
34494 //        var maxX = pos.right;
34495 //        
34496 //        var maxHeight = 0;
34497 //        
34498 //        Roo.each(items, function(item, k){
34499 //            
34500 //            var c = k % 2;
34501 //            
34502 //            item.el.position('absolute');
34503 //                
34504 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34505 //
34506 //            item.el.setWidth(width);
34507 //
34508 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34509 //
34510 //            item.el.setHeight(height);
34511 //            
34512 //            if(c == 0){
34513 //                item.el.setXY([x, y], isInstant ? false : true);
34514 //            } else {
34515 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34516 //            }
34517 //            
34518 //            y = y + height + this.alternativePadWidth;
34519 //            
34520 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34521 //            
34522 //        }, this);
34523 //        
34524 //        this.el.setHeight(maxHeight);
34525 //        
34526 //    },
34527     
34528     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34529     {
34530         var pos = this.el.getBox(true);
34531         
34532         var minX = pos.x;
34533         var minY = pos.y;
34534         
34535         var maxX = pos.right;
34536         
34537         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34538         
34539         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34540         
34541         Roo.each(queue, function(box, k){
34542             
34543             Roo.each(box, function(b, kk){
34544                 
34545                 b.el.position('absolute');
34546                 
34547                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34548                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34549                 
34550                 if(b.size == 'md-left' || b.size == 'md-right'){
34551                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34552                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34553                 }
34554                 
34555                 b.el.setWidth(width);
34556                 b.el.setHeight(height);
34557                 
34558             }, this);
34559             
34560             if(!box.length){
34561                 return;
34562             }
34563             
34564             var positions = [];
34565             
34566             switch (box.length){
34567                 case 1 :
34568                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34569                     break;
34570                 case 2 :
34571                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34572                     break;
34573                 case 3 :
34574                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34575                     break;
34576                 case 4 :
34577                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34578                     break;
34579                 default :
34580                     break;
34581             }
34582             
34583             Roo.each(box, function(b,kk){
34584                 
34585                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34586                 
34587                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34588                 
34589             }, this);
34590             
34591         }, this);
34592         
34593     },
34594     
34595     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34596     {
34597         Roo.each(eItems, function(b,k){
34598             
34599             b.size = (k == 0) ? 'sm' : 'xs';
34600             b.x = (k == 0) ? 2 : 1;
34601             b.y = (k == 0) ? 2 : 1;
34602             
34603             b.el.position('absolute');
34604             
34605             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34606                 
34607             b.el.setWidth(width);
34608             
34609             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34610             
34611             b.el.setHeight(height);
34612             
34613         }, this);
34614
34615         var positions = [];
34616         
34617         positions.push({
34618             x : maxX - this.unitWidth * 2 - this.gutter,
34619             y : minY
34620         });
34621         
34622         positions.push({
34623             x : maxX - this.unitWidth,
34624             y : minY + (this.unitWidth + this.gutter) * 2
34625         });
34626         
34627         positions.push({
34628             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34629             y : minY
34630         });
34631         
34632         Roo.each(eItems, function(b,k){
34633             
34634             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34635
34636         }, this);
34637         
34638     },
34639     
34640     getVerticalOneBoxColPositions : function(x, y, box)
34641     {
34642         var pos = [];
34643         
34644         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34645         
34646         if(box[0].size == 'md-left'){
34647             rand = 0;
34648         }
34649         
34650         if(box[0].size == 'md-right'){
34651             rand = 1;
34652         }
34653         
34654         pos.push({
34655             x : x + (this.unitWidth + this.gutter) * rand,
34656             y : y
34657         });
34658         
34659         return pos;
34660     },
34661     
34662     getVerticalTwoBoxColPositions : function(x, y, box)
34663     {
34664         var pos = [];
34665         
34666         if(box[0].size == 'xs'){
34667             
34668             pos.push({
34669                 x : x,
34670                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34671             });
34672
34673             pos.push({
34674                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34675                 y : y
34676             });
34677             
34678             return pos;
34679             
34680         }
34681         
34682         pos.push({
34683             x : x,
34684             y : y
34685         });
34686
34687         pos.push({
34688             x : x + (this.unitWidth + this.gutter) * 2,
34689             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34690         });
34691         
34692         return pos;
34693         
34694     },
34695     
34696     getVerticalThreeBoxColPositions : function(x, y, box)
34697     {
34698         var pos = [];
34699         
34700         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34701             
34702             pos.push({
34703                 x : x,
34704                 y : y
34705             });
34706
34707             pos.push({
34708                 x : x + (this.unitWidth + this.gutter) * 1,
34709                 y : y
34710             });
34711             
34712             pos.push({
34713                 x : x + (this.unitWidth + this.gutter) * 2,
34714                 y : y
34715             });
34716             
34717             return pos;
34718             
34719         }
34720         
34721         if(box[0].size == 'xs' && box[1].size == 'xs'){
34722             
34723             pos.push({
34724                 x : x,
34725                 y : y
34726             });
34727
34728             pos.push({
34729                 x : x,
34730                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34731             });
34732             
34733             pos.push({
34734                 x : x + (this.unitWidth + this.gutter) * 1,
34735                 y : y
34736             });
34737             
34738             return pos;
34739             
34740         }
34741         
34742         pos.push({
34743             x : x,
34744             y : y
34745         });
34746
34747         pos.push({
34748             x : x + (this.unitWidth + this.gutter) * 2,
34749             y : y
34750         });
34751
34752         pos.push({
34753             x : x + (this.unitWidth + this.gutter) * 2,
34754             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34755         });
34756             
34757         return pos;
34758         
34759     },
34760     
34761     getVerticalFourBoxColPositions : function(x, y, box)
34762     {
34763         var pos = [];
34764         
34765         if(box[0].size == 'xs'){
34766             
34767             pos.push({
34768                 x : x,
34769                 y : y
34770             });
34771
34772             pos.push({
34773                 x : x,
34774                 y : y + (this.unitHeight + this.gutter) * 1
34775             });
34776             
34777             pos.push({
34778                 x : x,
34779                 y : y + (this.unitHeight + this.gutter) * 2
34780             });
34781             
34782             pos.push({
34783                 x : x + (this.unitWidth + this.gutter) * 1,
34784                 y : y
34785             });
34786             
34787             return pos;
34788             
34789         }
34790         
34791         pos.push({
34792             x : x,
34793             y : y
34794         });
34795
34796         pos.push({
34797             x : x + (this.unitWidth + this.gutter) * 2,
34798             y : y
34799         });
34800
34801         pos.push({
34802             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34803             y : y + (this.unitHeight + this.gutter) * 1
34804         });
34805
34806         pos.push({
34807             x : x + (this.unitWidth + this.gutter) * 2,
34808             y : y + (this.unitWidth + this.gutter) * 2
34809         });
34810
34811         return pos;
34812         
34813     },
34814     
34815     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34816     {
34817         var pos = [];
34818         
34819         if(box[0].size == 'md-left'){
34820             pos.push({
34821                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34822                 y : minY
34823             });
34824             
34825             return pos;
34826         }
34827         
34828         if(box[0].size == 'md-right'){
34829             pos.push({
34830                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34831                 y : minY + (this.unitWidth + this.gutter) * 1
34832             });
34833             
34834             return pos;
34835         }
34836         
34837         var rand = Math.floor(Math.random() * (4 - box[0].y));
34838         
34839         pos.push({
34840             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34841             y : minY + (this.unitWidth + this.gutter) * rand
34842         });
34843         
34844         return pos;
34845         
34846     },
34847     
34848     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34849     {
34850         var pos = [];
34851         
34852         if(box[0].size == 'xs'){
34853             
34854             pos.push({
34855                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34856                 y : minY
34857             });
34858
34859             pos.push({
34860                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34861                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34862             });
34863             
34864             return pos;
34865             
34866         }
34867         
34868         pos.push({
34869             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34870             y : minY
34871         });
34872
34873         pos.push({
34874             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34875             y : minY + (this.unitWidth + this.gutter) * 2
34876         });
34877         
34878         return pos;
34879         
34880     },
34881     
34882     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34883     {
34884         var pos = [];
34885         
34886         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34887             
34888             pos.push({
34889                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34890                 y : minY
34891             });
34892
34893             pos.push({
34894                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34895                 y : minY + (this.unitWidth + this.gutter) * 1
34896             });
34897             
34898             pos.push({
34899                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34900                 y : minY + (this.unitWidth + this.gutter) * 2
34901             });
34902             
34903             return pos;
34904             
34905         }
34906         
34907         if(box[0].size == 'xs' && box[1].size == 'xs'){
34908             
34909             pos.push({
34910                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34911                 y : minY
34912             });
34913
34914             pos.push({
34915                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34916                 y : minY
34917             });
34918             
34919             pos.push({
34920                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34921                 y : minY + (this.unitWidth + this.gutter) * 1
34922             });
34923             
34924             return pos;
34925             
34926         }
34927         
34928         pos.push({
34929             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34930             y : minY
34931         });
34932
34933         pos.push({
34934             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34935             y : minY + (this.unitWidth + this.gutter) * 2
34936         });
34937
34938         pos.push({
34939             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34940             y : minY + (this.unitWidth + this.gutter) * 2
34941         });
34942             
34943         return pos;
34944         
34945     },
34946     
34947     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34948     {
34949         var pos = [];
34950         
34951         if(box[0].size == 'xs'){
34952             
34953             pos.push({
34954                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34955                 y : minY
34956             });
34957
34958             pos.push({
34959                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34960                 y : minY
34961             });
34962             
34963             pos.push({
34964                 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),
34965                 y : minY
34966             });
34967             
34968             pos.push({
34969                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34970                 y : minY + (this.unitWidth + this.gutter) * 1
34971             });
34972             
34973             return pos;
34974             
34975         }
34976         
34977         pos.push({
34978             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34979             y : minY
34980         });
34981         
34982         pos.push({
34983             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34984             y : minY + (this.unitWidth + this.gutter) * 2
34985         });
34986         
34987         pos.push({
34988             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34989             y : minY + (this.unitWidth + this.gutter) * 2
34990         });
34991         
34992         pos.push({
34993             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),
34994             y : minY + (this.unitWidth + this.gutter) * 2
34995         });
34996
34997         return pos;
34998         
34999     },
35000     
35001     /**
35002     * remove a Masonry Brick
35003     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35004     */
35005     removeBrick : function(brick_id)
35006     {
35007         if (!brick_id) {
35008             return;
35009         }
35010         
35011         for (var i = 0; i<this.bricks.length; i++) {
35012             if (this.bricks[i].id == brick_id) {
35013                 this.bricks.splice(i,1);
35014                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35015                 this.initial();
35016             }
35017         }
35018     },
35019     
35020     /**
35021     * adds a Masonry Brick
35022     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35023     */
35024     addBrick : function(cfg)
35025     {
35026         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35027         //this.register(cn);
35028         cn.parentId = this.id;
35029         cn.render(this.el);
35030         return cn;
35031     },
35032     
35033     /**
35034     * register a Masonry Brick
35035     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35036     */
35037     
35038     register : function(brick)
35039     {
35040         this.bricks.push(brick);
35041         brick.masonryId = this.id;
35042     },
35043     
35044     /**
35045     * clear all the Masonry Brick
35046     */
35047     clearAll : function()
35048     {
35049         this.bricks = [];
35050         //this.getChildContainer().dom.innerHTML = "";
35051         this.el.dom.innerHTML = '';
35052     },
35053     
35054     getSelected : function()
35055     {
35056         if (!this.selectedBrick) {
35057             return false;
35058         }
35059         
35060         return this.selectedBrick;
35061     }
35062 });
35063
35064 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35065     
35066     groups: {},
35067      /**
35068     * register a Masonry Layout
35069     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35070     */
35071     
35072     register : function(layout)
35073     {
35074         this.groups[layout.id] = layout;
35075     },
35076     /**
35077     * fetch a  Masonry Layout based on the masonry layout ID
35078     * @param {string} the masonry layout to add
35079     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35080     */
35081     
35082     get: function(layout_id) {
35083         if (typeof(this.groups[layout_id]) == 'undefined') {
35084             return false;
35085         }
35086         return this.groups[layout_id] ;
35087     }
35088     
35089     
35090     
35091 });
35092
35093  
35094
35095  /**
35096  *
35097  * This is based on 
35098  * http://masonry.desandro.com
35099  *
35100  * The idea is to render all the bricks based on vertical width...
35101  *
35102  * The original code extends 'outlayer' - we might need to use that....
35103  * 
35104  */
35105
35106
35107 /**
35108  * @class Roo.bootstrap.LayoutMasonryAuto
35109  * @extends Roo.bootstrap.Component
35110  * Bootstrap Layout Masonry class
35111  * 
35112  * @constructor
35113  * Create a new Element
35114  * @param {Object} config The config object
35115  */
35116
35117 Roo.bootstrap.LayoutMasonryAuto = function(config){
35118     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35119 };
35120
35121 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35122     
35123       /**
35124      * @cfg {Boolean} isFitWidth  - resize the width..
35125      */   
35126     isFitWidth : false,  // options..
35127     /**
35128      * @cfg {Boolean} isOriginLeft = left align?
35129      */   
35130     isOriginLeft : true,
35131     /**
35132      * @cfg {Boolean} isOriginTop = top align?
35133      */   
35134     isOriginTop : false,
35135     /**
35136      * @cfg {Boolean} isLayoutInstant = no animation?
35137      */   
35138     isLayoutInstant : false, // needed?
35139     /**
35140      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35141      */   
35142     isResizingContainer : true,
35143     /**
35144      * @cfg {Number} columnWidth  width of the columns 
35145      */   
35146     
35147     columnWidth : 0,
35148     
35149     /**
35150      * @cfg {Number} maxCols maximum number of columns
35151      */   
35152     
35153     maxCols: 0,
35154     /**
35155      * @cfg {Number} padHeight padding below box..
35156      */   
35157     
35158     padHeight : 10, 
35159     
35160     /**
35161      * @cfg {Boolean} isAutoInitial defalut true
35162      */   
35163     
35164     isAutoInitial : true, 
35165     
35166     // private?
35167     gutter : 0,
35168     
35169     containerWidth: 0,
35170     initialColumnWidth : 0,
35171     currentSize : null,
35172     
35173     colYs : null, // array.
35174     maxY : 0,
35175     padWidth: 10,
35176     
35177     
35178     tag: 'div',
35179     cls: '',
35180     bricks: null, //CompositeElement
35181     cols : 0, // array?
35182     // element : null, // wrapped now this.el
35183     _isLayoutInited : null, 
35184     
35185     
35186     getAutoCreate : function(){
35187         
35188         var cfg = {
35189             tag: this.tag,
35190             cls: 'blog-masonary-wrapper ' + this.cls,
35191             cn : {
35192                 cls : 'mas-boxes masonary'
35193             }
35194         };
35195         
35196         return cfg;
35197     },
35198     
35199     getChildContainer: function( )
35200     {
35201         if (this.boxesEl) {
35202             return this.boxesEl;
35203         }
35204         
35205         this.boxesEl = this.el.select('.mas-boxes').first();
35206         
35207         return this.boxesEl;
35208     },
35209     
35210     
35211     initEvents : function()
35212     {
35213         var _this = this;
35214         
35215         if(this.isAutoInitial){
35216             Roo.log('hook children rendered');
35217             this.on('childrenrendered', function() {
35218                 Roo.log('children rendered');
35219                 _this.initial();
35220             } ,this);
35221         }
35222         
35223     },
35224     
35225     initial : function()
35226     {
35227         this.reloadItems();
35228
35229         this.currentSize = this.el.getBox(true);
35230
35231         /// was window resize... - let's see if this works..
35232         Roo.EventManager.onWindowResize(this.resize, this); 
35233
35234         if(!this.isAutoInitial){
35235             this.layout();
35236             return;
35237         }
35238         
35239         this.layout.defer(500,this);
35240     },
35241     
35242     reloadItems: function()
35243     {
35244         this.bricks = this.el.select('.masonry-brick', true);
35245         
35246         this.bricks.each(function(b) {
35247             //Roo.log(b.getSize());
35248             if (!b.attr('originalwidth')) {
35249                 b.attr('originalwidth',  b.getSize().width);
35250             }
35251             
35252         });
35253         
35254         Roo.log(this.bricks.elements.length);
35255     },
35256     
35257     resize : function()
35258     {
35259         Roo.log('resize');
35260         var cs = this.el.getBox(true);
35261         
35262         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35263             Roo.log("no change in with or X");
35264             return;
35265         }
35266         this.currentSize = cs;
35267         this.layout();
35268     },
35269     
35270     layout : function()
35271     {
35272          Roo.log('layout');
35273         this._resetLayout();
35274         //this._manageStamps();
35275       
35276         // don't animate first layout
35277         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35278         this.layoutItems( isInstant );
35279       
35280         // flag for initalized
35281         this._isLayoutInited = true;
35282     },
35283     
35284     layoutItems : function( isInstant )
35285     {
35286         //var items = this._getItemsForLayout( this.items );
35287         // original code supports filtering layout items.. we just ignore it..
35288         
35289         this._layoutItems( this.bricks , isInstant );
35290       
35291         this._postLayout();
35292     },
35293     _layoutItems : function ( items , isInstant)
35294     {
35295        //this.fireEvent( 'layout', this, items );
35296     
35297
35298         if ( !items || !items.elements.length ) {
35299           // no items, emit event with empty array
35300             return;
35301         }
35302
35303         var queue = [];
35304         items.each(function(item) {
35305             Roo.log("layout item");
35306             Roo.log(item);
35307             // get x/y object from method
35308             var position = this._getItemLayoutPosition( item );
35309             // enqueue
35310             position.item = item;
35311             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35312             queue.push( position );
35313         }, this);
35314       
35315         this._processLayoutQueue( queue );
35316     },
35317     /** Sets position of item in DOM
35318     * @param {Element} item
35319     * @param {Number} x - horizontal position
35320     * @param {Number} y - vertical position
35321     * @param {Boolean} isInstant - disables transitions
35322     */
35323     _processLayoutQueue : function( queue )
35324     {
35325         for ( var i=0, len = queue.length; i < len; i++ ) {
35326             var obj = queue[i];
35327             obj.item.position('absolute');
35328             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35329         }
35330     },
35331       
35332     
35333     /**
35334     * Any logic you want to do after each layout,
35335     * i.e. size the container
35336     */
35337     _postLayout : function()
35338     {
35339         this.resizeContainer();
35340     },
35341     
35342     resizeContainer : function()
35343     {
35344         if ( !this.isResizingContainer ) {
35345             return;
35346         }
35347         var size = this._getContainerSize();
35348         if ( size ) {
35349             this.el.setSize(size.width,size.height);
35350             this.boxesEl.setSize(size.width,size.height);
35351         }
35352     },
35353     
35354     
35355     
35356     _resetLayout : function()
35357     {
35358         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35359         this.colWidth = this.el.getWidth();
35360         //this.gutter = this.el.getWidth(); 
35361         
35362         this.measureColumns();
35363
35364         // reset column Y
35365         var i = this.cols;
35366         this.colYs = [];
35367         while (i--) {
35368             this.colYs.push( 0 );
35369         }
35370     
35371         this.maxY = 0;
35372     },
35373
35374     measureColumns : function()
35375     {
35376         this.getContainerWidth();
35377       // if columnWidth is 0, default to outerWidth of first item
35378         if ( !this.columnWidth ) {
35379             var firstItem = this.bricks.first();
35380             Roo.log(firstItem);
35381             this.columnWidth  = this.containerWidth;
35382             if (firstItem && firstItem.attr('originalwidth') ) {
35383                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35384             }
35385             // columnWidth fall back to item of first element
35386             Roo.log("set column width?");
35387                         this.initialColumnWidth = this.columnWidth  ;
35388
35389             // if first elem has no width, default to size of container
35390             
35391         }
35392         
35393         
35394         if (this.initialColumnWidth) {
35395             this.columnWidth = this.initialColumnWidth;
35396         }
35397         
35398         
35399             
35400         // column width is fixed at the top - however if container width get's smaller we should
35401         // reduce it...
35402         
35403         // this bit calcs how man columns..
35404             
35405         var columnWidth = this.columnWidth += this.gutter;
35406       
35407         // calculate columns
35408         var containerWidth = this.containerWidth + this.gutter;
35409         
35410         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35411         // fix rounding errors, typically with gutters
35412         var excess = columnWidth - containerWidth % columnWidth;
35413         
35414         
35415         // if overshoot is less than a pixel, round up, otherwise floor it
35416         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35417         cols = Math[ mathMethod ]( cols );
35418         this.cols = Math.max( cols, 1 );
35419         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35420         
35421          // padding positioning..
35422         var totalColWidth = this.cols * this.columnWidth;
35423         var padavail = this.containerWidth - totalColWidth;
35424         // so for 2 columns - we need 3 'pads'
35425         
35426         var padNeeded = (1+this.cols) * this.padWidth;
35427         
35428         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35429         
35430         this.columnWidth += padExtra
35431         //this.padWidth = Math.floor(padavail /  ( this.cols));
35432         
35433         // adjust colum width so that padding is fixed??
35434         
35435         // we have 3 columns ... total = width * 3
35436         // we have X left over... that should be used by 
35437         
35438         //if (this.expandC) {
35439             
35440         //}
35441         
35442         
35443         
35444     },
35445     
35446     getContainerWidth : function()
35447     {
35448        /* // container is parent if fit width
35449         var container = this.isFitWidth ? this.element.parentNode : this.element;
35450         // check that this.size and size are there
35451         // IE8 triggers resize on body size change, so they might not be
35452         
35453         var size = getSize( container );  //FIXME
35454         this.containerWidth = size && size.innerWidth; //FIXME
35455         */
35456          
35457         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35458         
35459     },
35460     
35461     _getItemLayoutPosition : function( item )  // what is item?
35462     {
35463         // we resize the item to our columnWidth..
35464       
35465         item.setWidth(this.columnWidth);
35466         item.autoBoxAdjust  = false;
35467         
35468         var sz = item.getSize();
35469  
35470         // how many columns does this brick span
35471         var remainder = this.containerWidth % this.columnWidth;
35472         
35473         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35474         // round if off by 1 pixel, otherwise use ceil
35475         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35476         colSpan = Math.min( colSpan, this.cols );
35477         
35478         // normally this should be '1' as we dont' currently allow multi width columns..
35479         
35480         var colGroup = this._getColGroup( colSpan );
35481         // get the minimum Y value from the columns
35482         var minimumY = Math.min.apply( Math, colGroup );
35483         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35484         
35485         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35486          
35487         // position the brick
35488         var position = {
35489             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35490             y: this.currentSize.y + minimumY + this.padHeight
35491         };
35492         
35493         Roo.log(position);
35494         // apply setHeight to necessary columns
35495         var setHeight = minimumY + sz.height + this.padHeight;
35496         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35497         
35498         var setSpan = this.cols + 1 - colGroup.length;
35499         for ( var i = 0; i < setSpan; i++ ) {
35500           this.colYs[ shortColIndex + i ] = setHeight ;
35501         }
35502       
35503         return position;
35504     },
35505     
35506     /**
35507      * @param {Number} colSpan - number of columns the element spans
35508      * @returns {Array} colGroup
35509      */
35510     _getColGroup : function( colSpan )
35511     {
35512         if ( colSpan < 2 ) {
35513           // if brick spans only one column, use all the column Ys
35514           return this.colYs;
35515         }
35516       
35517         var colGroup = [];
35518         // how many different places could this brick fit horizontally
35519         var groupCount = this.cols + 1 - colSpan;
35520         // for each group potential horizontal position
35521         for ( var i = 0; i < groupCount; i++ ) {
35522           // make an array of colY values for that one group
35523           var groupColYs = this.colYs.slice( i, i + colSpan );
35524           // and get the max value of the array
35525           colGroup[i] = Math.max.apply( Math, groupColYs );
35526         }
35527         return colGroup;
35528     },
35529     /*
35530     _manageStamp : function( stamp )
35531     {
35532         var stampSize =  stamp.getSize();
35533         var offset = stamp.getBox();
35534         // get the columns that this stamp affects
35535         var firstX = this.isOriginLeft ? offset.x : offset.right;
35536         var lastX = firstX + stampSize.width;
35537         var firstCol = Math.floor( firstX / this.columnWidth );
35538         firstCol = Math.max( 0, firstCol );
35539         
35540         var lastCol = Math.floor( lastX / this.columnWidth );
35541         // lastCol should not go over if multiple of columnWidth #425
35542         lastCol -= lastX % this.columnWidth ? 0 : 1;
35543         lastCol = Math.min( this.cols - 1, lastCol );
35544         
35545         // set colYs to bottom of the stamp
35546         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35547             stampSize.height;
35548             
35549         for ( var i = firstCol; i <= lastCol; i++ ) {
35550           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35551         }
35552     },
35553     */
35554     
35555     _getContainerSize : function()
35556     {
35557         this.maxY = Math.max.apply( Math, this.colYs );
35558         var size = {
35559             height: this.maxY
35560         };
35561       
35562         if ( this.isFitWidth ) {
35563             size.width = this._getContainerFitWidth();
35564         }
35565       
35566         return size;
35567     },
35568     
35569     _getContainerFitWidth : function()
35570     {
35571         var unusedCols = 0;
35572         // count unused columns
35573         var i = this.cols;
35574         while ( --i ) {
35575           if ( this.colYs[i] !== 0 ) {
35576             break;
35577           }
35578           unusedCols++;
35579         }
35580         // fit container to columns that have been used
35581         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35582     },
35583     
35584     needsResizeLayout : function()
35585     {
35586         var previousWidth = this.containerWidth;
35587         this.getContainerWidth();
35588         return previousWidth !== this.containerWidth;
35589     }
35590  
35591 });
35592
35593  
35594
35595  /*
35596  * - LGPL
35597  *
35598  * element
35599  * 
35600  */
35601
35602 /**
35603  * @class Roo.bootstrap.MasonryBrick
35604  * @extends Roo.bootstrap.Component
35605  * Bootstrap MasonryBrick class
35606  * 
35607  * @constructor
35608  * Create a new MasonryBrick
35609  * @param {Object} config The config object
35610  */
35611
35612 Roo.bootstrap.MasonryBrick = function(config){
35613     
35614     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35615     
35616     Roo.bootstrap.MasonryBrick.register(this);
35617     
35618     this.addEvents({
35619         // raw events
35620         /**
35621          * @event click
35622          * When a MasonryBrick is clcik
35623          * @param {Roo.bootstrap.MasonryBrick} this
35624          * @param {Roo.EventObject} e
35625          */
35626         "click" : true
35627     });
35628 };
35629
35630 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35631     
35632     /**
35633      * @cfg {String} title
35634      */   
35635     title : '',
35636     /**
35637      * @cfg {String} html
35638      */   
35639     html : '',
35640     /**
35641      * @cfg {String} bgimage
35642      */   
35643     bgimage : '',
35644     /**
35645      * @cfg {String} videourl
35646      */   
35647     videourl : '',
35648     /**
35649      * @cfg {String} cls
35650      */   
35651     cls : '',
35652     /**
35653      * @cfg {String} href
35654      */   
35655     href : '',
35656     /**
35657      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35658      */   
35659     size : 'xs',
35660     
35661     /**
35662      * @cfg {String} placetitle (center|bottom)
35663      */   
35664     placetitle : '',
35665     
35666     /**
35667      * @cfg {Boolean} isFitContainer defalut true
35668      */   
35669     isFitContainer : true, 
35670     
35671     /**
35672      * @cfg {Boolean} preventDefault defalut false
35673      */   
35674     preventDefault : false, 
35675     
35676     /**
35677      * @cfg {Boolean} inverse defalut false
35678      */   
35679     maskInverse : false, 
35680     
35681     getAutoCreate : function()
35682     {
35683         if(!this.isFitContainer){
35684             return this.getSplitAutoCreate();
35685         }
35686         
35687         var cls = 'masonry-brick masonry-brick-full';
35688         
35689         if(this.href.length){
35690             cls += ' masonry-brick-link';
35691         }
35692         
35693         if(this.bgimage.length){
35694             cls += ' masonry-brick-image';
35695         }
35696         
35697         if(this.maskInverse){
35698             cls += ' mask-inverse';
35699         }
35700         
35701         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35702             cls += ' enable-mask';
35703         }
35704         
35705         if(this.size){
35706             cls += ' masonry-' + this.size + '-brick';
35707         }
35708         
35709         if(this.placetitle.length){
35710             
35711             switch (this.placetitle) {
35712                 case 'center' :
35713                     cls += ' masonry-center-title';
35714                     break;
35715                 case 'bottom' :
35716                     cls += ' masonry-bottom-title';
35717                     break;
35718                 default:
35719                     break;
35720             }
35721             
35722         } else {
35723             if(!this.html.length && !this.bgimage.length){
35724                 cls += ' masonry-center-title';
35725             }
35726
35727             if(!this.html.length && this.bgimage.length){
35728                 cls += ' masonry-bottom-title';
35729             }
35730         }
35731         
35732         if(this.cls){
35733             cls += ' ' + this.cls;
35734         }
35735         
35736         var cfg = {
35737             tag: (this.href.length) ? 'a' : 'div',
35738             cls: cls,
35739             cn: [
35740                 {
35741                     tag: 'div',
35742                     cls: 'masonry-brick-mask'
35743                 },
35744                 {
35745                     tag: 'div',
35746                     cls: 'masonry-brick-paragraph',
35747                     cn: []
35748                 }
35749             ]
35750         };
35751         
35752         if(this.href.length){
35753             cfg.href = this.href;
35754         }
35755         
35756         var cn = cfg.cn[1].cn;
35757         
35758         if(this.title.length){
35759             cn.push({
35760                 tag: 'h4',
35761                 cls: 'masonry-brick-title',
35762                 html: this.title
35763             });
35764         }
35765         
35766         if(this.html.length){
35767             cn.push({
35768                 tag: 'p',
35769                 cls: 'masonry-brick-text',
35770                 html: this.html
35771             });
35772         }
35773         
35774         if (!this.title.length && !this.html.length) {
35775             cfg.cn[1].cls += ' hide';
35776         }
35777         
35778         if(this.bgimage.length){
35779             cfg.cn.push({
35780                 tag: 'img',
35781                 cls: 'masonry-brick-image-view',
35782                 src: this.bgimage
35783             });
35784         }
35785         
35786         if(this.videourl.length){
35787             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35788             // youtube support only?
35789             cfg.cn.push({
35790                 tag: 'iframe',
35791                 cls: 'masonry-brick-image-view',
35792                 src: vurl,
35793                 frameborder : 0,
35794                 allowfullscreen : true
35795             });
35796         }
35797         
35798         return cfg;
35799         
35800     },
35801     
35802     getSplitAutoCreate : function()
35803     {
35804         var cls = 'masonry-brick masonry-brick-split';
35805         
35806         if(this.href.length){
35807             cls += ' masonry-brick-link';
35808         }
35809         
35810         if(this.bgimage.length){
35811             cls += ' masonry-brick-image';
35812         }
35813         
35814         if(this.size){
35815             cls += ' masonry-' + this.size + '-brick';
35816         }
35817         
35818         switch (this.placetitle) {
35819             case 'center' :
35820                 cls += ' masonry-center-title';
35821                 break;
35822             case 'bottom' :
35823                 cls += ' masonry-bottom-title';
35824                 break;
35825             default:
35826                 if(!this.bgimage.length){
35827                     cls += ' masonry-center-title';
35828                 }
35829
35830                 if(this.bgimage.length){
35831                     cls += ' masonry-bottom-title';
35832                 }
35833                 break;
35834         }
35835         
35836         if(this.cls){
35837             cls += ' ' + this.cls;
35838         }
35839         
35840         var cfg = {
35841             tag: (this.href.length) ? 'a' : 'div',
35842             cls: cls,
35843             cn: [
35844                 {
35845                     tag: 'div',
35846                     cls: 'masonry-brick-split-head',
35847                     cn: [
35848                         {
35849                             tag: 'div',
35850                             cls: 'masonry-brick-paragraph',
35851                             cn: []
35852                         }
35853                     ]
35854                 },
35855                 {
35856                     tag: 'div',
35857                     cls: 'masonry-brick-split-body',
35858                     cn: []
35859                 }
35860             ]
35861         };
35862         
35863         if(this.href.length){
35864             cfg.href = this.href;
35865         }
35866         
35867         if(this.title.length){
35868             cfg.cn[0].cn[0].cn.push({
35869                 tag: 'h4',
35870                 cls: 'masonry-brick-title',
35871                 html: this.title
35872             });
35873         }
35874         
35875         if(this.html.length){
35876             cfg.cn[1].cn.push({
35877                 tag: 'p',
35878                 cls: 'masonry-brick-text',
35879                 html: this.html
35880             });
35881         }
35882
35883         if(this.bgimage.length){
35884             cfg.cn[0].cn.push({
35885                 tag: 'img',
35886                 cls: 'masonry-brick-image-view',
35887                 src: this.bgimage
35888             });
35889         }
35890         
35891         if(this.videourl.length){
35892             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35893             // youtube support only?
35894             cfg.cn[0].cn.cn.push({
35895                 tag: 'iframe',
35896                 cls: 'masonry-brick-image-view',
35897                 src: vurl,
35898                 frameborder : 0,
35899                 allowfullscreen : true
35900             });
35901         }
35902         
35903         return cfg;
35904     },
35905     
35906     initEvents: function() 
35907     {
35908         switch (this.size) {
35909             case 'xs' :
35910                 this.x = 1;
35911                 this.y = 1;
35912                 break;
35913             case 'sm' :
35914                 this.x = 2;
35915                 this.y = 2;
35916                 break;
35917             case 'md' :
35918             case 'md-left' :
35919             case 'md-right' :
35920                 this.x = 3;
35921                 this.y = 3;
35922                 break;
35923             case 'tall' :
35924                 this.x = 2;
35925                 this.y = 3;
35926                 break;
35927             case 'wide' :
35928                 this.x = 3;
35929                 this.y = 2;
35930                 break;
35931             case 'wide-thin' :
35932                 this.x = 3;
35933                 this.y = 1;
35934                 break;
35935                         
35936             default :
35937                 break;
35938         }
35939         
35940         if(Roo.isTouch){
35941             this.el.on('touchstart', this.onTouchStart, this);
35942             this.el.on('touchmove', this.onTouchMove, this);
35943             this.el.on('touchend', this.onTouchEnd, this);
35944             this.el.on('contextmenu', this.onContextMenu, this);
35945         } else {
35946             this.el.on('mouseenter'  ,this.enter, this);
35947             this.el.on('mouseleave', this.leave, this);
35948             this.el.on('click', this.onClick, this);
35949         }
35950         
35951         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35952             this.parent().bricks.push(this);   
35953         }
35954         
35955     },
35956     
35957     onClick: function(e, el)
35958     {
35959         var time = this.endTimer - this.startTimer;
35960         // Roo.log(e.preventDefault());
35961         if(Roo.isTouch){
35962             if(time > 1000){
35963                 e.preventDefault();
35964                 return;
35965             }
35966         }
35967         
35968         if(!this.preventDefault){
35969             return;
35970         }
35971         
35972         e.preventDefault();
35973         
35974         if (this.activeClass != '') {
35975             this.selectBrick();
35976         }
35977         
35978         this.fireEvent('click', this, e);
35979     },
35980     
35981     enter: function(e, el)
35982     {
35983         e.preventDefault();
35984         
35985         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35986             return;
35987         }
35988         
35989         if(this.bgimage.length && this.html.length){
35990             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35991         }
35992     },
35993     
35994     leave: function(e, el)
35995     {
35996         e.preventDefault();
35997         
35998         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35999             return;
36000         }
36001         
36002         if(this.bgimage.length && this.html.length){
36003             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36004         }
36005     },
36006     
36007     onTouchStart: function(e, el)
36008     {
36009 //        e.preventDefault();
36010         
36011         this.touchmoved = false;
36012         
36013         if(!this.isFitContainer){
36014             return;
36015         }
36016         
36017         if(!this.bgimage.length || !this.html.length){
36018             return;
36019         }
36020         
36021         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36022         
36023         this.timer = new Date().getTime();
36024         
36025     },
36026     
36027     onTouchMove: function(e, el)
36028     {
36029         this.touchmoved = true;
36030     },
36031     
36032     onContextMenu : function(e,el)
36033     {
36034         e.preventDefault();
36035         e.stopPropagation();
36036         return false;
36037     },
36038     
36039     onTouchEnd: function(e, el)
36040     {
36041 //        e.preventDefault();
36042         
36043         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36044         
36045             this.leave(e,el);
36046             
36047             return;
36048         }
36049         
36050         if(!this.bgimage.length || !this.html.length){
36051             
36052             if(this.href.length){
36053                 window.location.href = this.href;
36054             }
36055             
36056             return;
36057         }
36058         
36059         if(!this.isFitContainer){
36060             return;
36061         }
36062         
36063         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36064         
36065         window.location.href = this.href;
36066     },
36067     
36068     //selection on single brick only
36069     selectBrick : function() {
36070         
36071         if (!this.parentId) {
36072             return;
36073         }
36074         
36075         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36076         var index = m.selectedBrick.indexOf(this.id);
36077         
36078         if ( index > -1) {
36079             m.selectedBrick.splice(index,1);
36080             this.el.removeClass(this.activeClass);
36081             return;
36082         }
36083         
36084         for(var i = 0; i < m.selectedBrick.length; i++) {
36085             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36086             b.el.removeClass(b.activeClass);
36087         }
36088         
36089         m.selectedBrick = [];
36090         
36091         m.selectedBrick.push(this.id);
36092         this.el.addClass(this.activeClass);
36093         return;
36094     },
36095     
36096     isSelected : function(){
36097         return this.el.hasClass(this.activeClass);
36098         
36099     }
36100 });
36101
36102 Roo.apply(Roo.bootstrap.MasonryBrick, {
36103     
36104     //groups: {},
36105     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36106      /**
36107     * register a Masonry Brick
36108     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36109     */
36110     
36111     register : function(brick)
36112     {
36113         //this.groups[brick.id] = brick;
36114         this.groups.add(brick.id, brick);
36115     },
36116     /**
36117     * fetch a  masonry brick based on the masonry brick ID
36118     * @param {string} the masonry brick to add
36119     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36120     */
36121     
36122     get: function(brick_id) 
36123     {
36124         // if (typeof(this.groups[brick_id]) == 'undefined') {
36125         //     return false;
36126         // }
36127         // return this.groups[brick_id] ;
36128         
36129         if(this.groups.key(brick_id)) {
36130             return this.groups.key(brick_id);
36131         }
36132         
36133         return false;
36134     }
36135     
36136     
36137     
36138 });
36139
36140  /*
36141  * - LGPL
36142  *
36143  * element
36144  * 
36145  */
36146
36147 /**
36148  * @class Roo.bootstrap.Brick
36149  * @extends Roo.bootstrap.Component
36150  * Bootstrap Brick class
36151  * 
36152  * @constructor
36153  * Create a new Brick
36154  * @param {Object} config The config object
36155  */
36156
36157 Roo.bootstrap.Brick = function(config){
36158     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36159     
36160     this.addEvents({
36161         // raw events
36162         /**
36163          * @event click
36164          * When a Brick is click
36165          * @param {Roo.bootstrap.Brick} this
36166          * @param {Roo.EventObject} e
36167          */
36168         "click" : true
36169     });
36170 };
36171
36172 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36173     
36174     /**
36175      * @cfg {String} title
36176      */   
36177     title : '',
36178     /**
36179      * @cfg {String} html
36180      */   
36181     html : '',
36182     /**
36183      * @cfg {String} bgimage
36184      */   
36185     bgimage : '',
36186     /**
36187      * @cfg {String} cls
36188      */   
36189     cls : '',
36190     /**
36191      * @cfg {String} href
36192      */   
36193     href : '',
36194     /**
36195      * @cfg {String} video
36196      */   
36197     video : '',
36198     /**
36199      * @cfg {Boolean} square
36200      */   
36201     square : true,
36202     
36203     getAutoCreate : function()
36204     {
36205         var cls = 'roo-brick';
36206         
36207         if(this.href.length){
36208             cls += ' roo-brick-link';
36209         }
36210         
36211         if(this.bgimage.length){
36212             cls += ' roo-brick-image';
36213         }
36214         
36215         if(!this.html.length && !this.bgimage.length){
36216             cls += ' roo-brick-center-title';
36217         }
36218         
36219         if(!this.html.length && this.bgimage.length){
36220             cls += ' roo-brick-bottom-title';
36221         }
36222         
36223         if(this.cls){
36224             cls += ' ' + this.cls;
36225         }
36226         
36227         var cfg = {
36228             tag: (this.href.length) ? 'a' : 'div',
36229             cls: cls,
36230             cn: [
36231                 {
36232                     tag: 'div',
36233                     cls: 'roo-brick-paragraph',
36234                     cn: []
36235                 }
36236             ]
36237         };
36238         
36239         if(this.href.length){
36240             cfg.href = this.href;
36241         }
36242         
36243         var cn = cfg.cn[0].cn;
36244         
36245         if(this.title.length){
36246             cn.push({
36247                 tag: 'h4',
36248                 cls: 'roo-brick-title',
36249                 html: this.title
36250             });
36251         }
36252         
36253         if(this.html.length){
36254             cn.push({
36255                 tag: 'p',
36256                 cls: 'roo-brick-text',
36257                 html: this.html
36258             });
36259         } else {
36260             cn.cls += ' hide';
36261         }
36262         
36263         if(this.bgimage.length){
36264             cfg.cn.push({
36265                 tag: 'img',
36266                 cls: 'roo-brick-image-view',
36267                 src: this.bgimage
36268             });
36269         }
36270         
36271         return cfg;
36272     },
36273     
36274     initEvents: function() 
36275     {
36276         if(this.title.length || this.html.length){
36277             this.el.on('mouseenter'  ,this.enter, this);
36278             this.el.on('mouseleave', this.leave, this);
36279         }
36280         
36281         Roo.EventManager.onWindowResize(this.resize, this); 
36282         
36283         if(this.bgimage.length){
36284             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36285             this.imageEl.on('load', this.onImageLoad, this);
36286             return;
36287         }
36288         
36289         this.resize();
36290     },
36291     
36292     onImageLoad : function()
36293     {
36294         this.resize();
36295     },
36296     
36297     resize : function()
36298     {
36299         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36300         
36301         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36302         
36303         if(this.bgimage.length){
36304             var image = this.el.select('.roo-brick-image-view', true).first();
36305             
36306             image.setWidth(paragraph.getWidth());
36307             
36308             if(this.square){
36309                 image.setHeight(paragraph.getWidth());
36310             }
36311             
36312             this.el.setHeight(image.getHeight());
36313             paragraph.setHeight(image.getHeight());
36314             
36315         }
36316         
36317     },
36318     
36319     enter: function(e, el)
36320     {
36321         e.preventDefault();
36322         
36323         if(this.bgimage.length){
36324             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36325             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36326         }
36327     },
36328     
36329     leave: function(e, el)
36330     {
36331         e.preventDefault();
36332         
36333         if(this.bgimage.length){
36334             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36335             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36336         }
36337     }
36338     
36339 });
36340
36341  
36342
36343  /*
36344  * - LGPL
36345  *
36346  * Number field 
36347  */
36348
36349 /**
36350  * @class Roo.bootstrap.NumberField
36351  * @extends Roo.bootstrap.Input
36352  * Bootstrap NumberField class
36353  * 
36354  * 
36355  * 
36356  * 
36357  * @constructor
36358  * Create a new NumberField
36359  * @param {Object} config The config object
36360  */
36361
36362 Roo.bootstrap.NumberField = function(config){
36363     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36364 };
36365
36366 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36367     
36368     /**
36369      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36370      */
36371     allowDecimals : true,
36372     /**
36373      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36374      */
36375     decimalSeparator : ".",
36376     /**
36377      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36378      */
36379     decimalPrecision : 2,
36380     /**
36381      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36382      */
36383     allowNegative : true,
36384     
36385     /**
36386      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36387      */
36388     allowZero: true,
36389     /**
36390      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36391      */
36392     minValue : Number.NEGATIVE_INFINITY,
36393     /**
36394      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36395      */
36396     maxValue : Number.MAX_VALUE,
36397     /**
36398      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36399      */
36400     minText : "The minimum value for this field is {0}",
36401     /**
36402      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36403      */
36404     maxText : "The maximum value for this field is {0}",
36405     /**
36406      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36407      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36408      */
36409     nanText : "{0} is not a valid number",
36410     /**
36411      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36412      */
36413     thousandsDelimiter : false,
36414     /**
36415      * @cfg {String} valueAlign alignment of value
36416      */
36417     valueAlign : "left",
36418
36419     getAutoCreate : function()
36420     {
36421         var hiddenInput = {
36422             tag: 'input',
36423             type: 'hidden',
36424             id: Roo.id(),
36425             cls: 'hidden-number-input'
36426         };
36427         
36428         if (this.name) {
36429             hiddenInput.name = this.name;
36430         }
36431         
36432         this.name = '';
36433         
36434         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36435         
36436         this.name = hiddenInput.name;
36437         
36438         if(cfg.cn.length > 0) {
36439             cfg.cn.push(hiddenInput);
36440         }
36441         
36442         return cfg;
36443     },
36444
36445     // private
36446     initEvents : function()
36447     {   
36448         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36449         
36450         var allowed = "0123456789";
36451         
36452         if(this.allowDecimals){
36453             allowed += this.decimalSeparator;
36454         }
36455         
36456         if(this.allowNegative){
36457             allowed += "-";
36458         }
36459         
36460         if(this.thousandsDelimiter) {
36461             allowed += ",";
36462         }
36463         
36464         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36465         
36466         var keyPress = function(e){
36467             
36468             var k = e.getKey();
36469             
36470             var c = e.getCharCode();
36471             
36472             if(
36473                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36474                     allowed.indexOf(String.fromCharCode(c)) === -1
36475             ){
36476                 e.stopEvent();
36477                 return;
36478             }
36479             
36480             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36481                 return;
36482             }
36483             
36484             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36485                 e.stopEvent();
36486             }
36487         };
36488         
36489         this.el.on("keypress", keyPress, this);
36490     },
36491     
36492     validateValue : function(value)
36493     {
36494         
36495         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36496             return false;
36497         }
36498         
36499         var num = this.parseValue(value);
36500         
36501         if(isNaN(num)){
36502             this.markInvalid(String.format(this.nanText, value));
36503             return false;
36504         }
36505         
36506         if(num < this.minValue){
36507             this.markInvalid(String.format(this.minText, this.minValue));
36508             return false;
36509         }
36510         
36511         if(num > this.maxValue){
36512             this.markInvalid(String.format(this.maxText, this.maxValue));
36513             return false;
36514         }
36515         
36516         return true;
36517     },
36518
36519     getValue : function()
36520     {
36521         var v = this.hiddenEl().getValue();
36522         
36523         return this.fixPrecision(this.parseValue(v));
36524     },
36525
36526     parseValue : function(value)
36527     {
36528         if(this.thousandsDelimiter) {
36529             value += "";
36530             r = new RegExp(",", "g");
36531             value = value.replace(r, "");
36532         }
36533         
36534         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36535         return isNaN(value) ? '' : value;
36536     },
36537
36538     fixPrecision : function(value)
36539     {
36540         if(this.thousandsDelimiter) {
36541             value += "";
36542             r = new RegExp(",", "g");
36543             value = value.replace(r, "");
36544         }
36545         
36546         var nan = isNaN(value);
36547         
36548         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36549             return nan ? '' : value;
36550         }
36551         return parseFloat(value).toFixed(this.decimalPrecision);
36552     },
36553
36554     setValue : function(v)
36555     {
36556         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36557         
36558         this.value = v;
36559         
36560         if(this.rendered){
36561             
36562             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36563             
36564             this.inputEl().dom.value = (v == '') ? '' :
36565                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36566             
36567             if(!this.allowZero && v === '0') {
36568                 this.hiddenEl().dom.value = '';
36569                 this.inputEl().dom.value = '';
36570             }
36571             
36572             this.validate();
36573         }
36574     },
36575
36576     decimalPrecisionFcn : function(v)
36577     {
36578         return Math.floor(v);
36579     },
36580
36581     beforeBlur : function()
36582     {
36583         var v = this.parseValue(this.getRawValue());
36584         
36585         if(v || v === 0 || v === ''){
36586             this.setValue(v);
36587         }
36588     },
36589     
36590     hiddenEl : function()
36591     {
36592         return this.el.select('input.hidden-number-input',true).first();
36593     }
36594     
36595 });
36596
36597  
36598
36599 /*
36600 * Licence: LGPL
36601 */
36602
36603 /**
36604  * @class Roo.bootstrap.DocumentSlider
36605  * @extends Roo.bootstrap.Component
36606  * Bootstrap DocumentSlider class
36607  * 
36608  * @constructor
36609  * Create a new DocumentViewer
36610  * @param {Object} config The config object
36611  */
36612
36613 Roo.bootstrap.DocumentSlider = function(config){
36614     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36615     
36616     this.files = [];
36617     
36618     this.addEvents({
36619         /**
36620          * @event initial
36621          * Fire after initEvent
36622          * @param {Roo.bootstrap.DocumentSlider} this
36623          */
36624         "initial" : true,
36625         /**
36626          * @event update
36627          * Fire after update
36628          * @param {Roo.bootstrap.DocumentSlider} this
36629          */
36630         "update" : true,
36631         /**
36632          * @event click
36633          * Fire after click
36634          * @param {Roo.bootstrap.DocumentSlider} this
36635          */
36636         "click" : true
36637     });
36638 };
36639
36640 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36641     
36642     files : false,
36643     
36644     indicator : 0,
36645     
36646     getAutoCreate : function()
36647     {
36648         var cfg = {
36649             tag : 'div',
36650             cls : 'roo-document-slider',
36651             cn : [
36652                 {
36653                     tag : 'div',
36654                     cls : 'roo-document-slider-header',
36655                     cn : [
36656                         {
36657                             tag : 'div',
36658                             cls : 'roo-document-slider-header-title'
36659                         }
36660                     ]
36661                 },
36662                 {
36663                     tag : 'div',
36664                     cls : 'roo-document-slider-body',
36665                     cn : [
36666                         {
36667                             tag : 'div',
36668                             cls : 'roo-document-slider-prev',
36669                             cn : [
36670                                 {
36671                                     tag : 'i',
36672                                     cls : 'fa fa-chevron-left'
36673                                 }
36674                             ]
36675                         },
36676                         {
36677                             tag : 'div',
36678                             cls : 'roo-document-slider-thumb',
36679                             cn : [
36680                                 {
36681                                     tag : 'img',
36682                                     cls : 'roo-document-slider-image'
36683                                 }
36684                             ]
36685                         },
36686                         {
36687                             tag : 'div',
36688                             cls : 'roo-document-slider-next',
36689                             cn : [
36690                                 {
36691                                     tag : 'i',
36692                                     cls : 'fa fa-chevron-right'
36693                                 }
36694                             ]
36695                         }
36696                     ]
36697                 }
36698             ]
36699         };
36700         
36701         return cfg;
36702     },
36703     
36704     initEvents : function()
36705     {
36706         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36707         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36708         
36709         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36710         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36711         
36712         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36713         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36714         
36715         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36716         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36717         
36718         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36719         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36720         
36721         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36722         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36723         
36724         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36725         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36726         
36727         this.thumbEl.on('click', this.onClick, this);
36728         
36729         this.prevIndicator.on('click', this.prev, this);
36730         
36731         this.nextIndicator.on('click', this.next, this);
36732         
36733     },
36734     
36735     initial : function()
36736     {
36737         if(this.files.length){
36738             this.indicator = 1;
36739             this.update()
36740         }
36741         
36742         this.fireEvent('initial', this);
36743     },
36744     
36745     update : function()
36746     {
36747         this.imageEl.attr('src', this.files[this.indicator - 1]);
36748         
36749         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36750         
36751         this.prevIndicator.show();
36752         
36753         if(this.indicator == 1){
36754             this.prevIndicator.hide();
36755         }
36756         
36757         this.nextIndicator.show();
36758         
36759         if(this.indicator == this.files.length){
36760             this.nextIndicator.hide();
36761         }
36762         
36763         this.thumbEl.scrollTo('top');
36764         
36765         this.fireEvent('update', this);
36766     },
36767     
36768     onClick : function(e)
36769     {
36770         e.preventDefault();
36771         
36772         this.fireEvent('click', this);
36773     },
36774     
36775     prev : function(e)
36776     {
36777         e.preventDefault();
36778         
36779         this.indicator = Math.max(1, this.indicator - 1);
36780         
36781         this.update();
36782     },
36783     
36784     next : function(e)
36785     {
36786         e.preventDefault();
36787         
36788         this.indicator = Math.min(this.files.length, this.indicator + 1);
36789         
36790         this.update();
36791     }
36792 });
36793 /*
36794  * - LGPL
36795  *
36796  * RadioSet
36797  *
36798  *
36799  */
36800
36801 /**
36802  * @class Roo.bootstrap.RadioSet
36803  * @extends Roo.bootstrap.Input
36804  * Bootstrap RadioSet class
36805  * @cfg {String} indicatorpos (left|right) default left
36806  * @cfg {Boolean} inline (true|false) inline the element (default true)
36807  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36808  * @constructor
36809  * Create a new RadioSet
36810  * @param {Object} config The config object
36811  */
36812
36813 Roo.bootstrap.RadioSet = function(config){
36814     
36815     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36816     
36817     this.radioes = [];
36818     
36819     Roo.bootstrap.RadioSet.register(this);
36820     
36821     this.addEvents({
36822         /**
36823         * @event check
36824         * Fires when the element is checked or unchecked.
36825         * @param {Roo.bootstrap.RadioSet} this This radio
36826         * @param {Roo.bootstrap.Radio} item The checked item
36827         */
36828        check : true,
36829        /**
36830         * @event click
36831         * Fires when the element is click.
36832         * @param {Roo.bootstrap.RadioSet} this This radio set
36833         * @param {Roo.bootstrap.Radio} item The checked item
36834         * @param {Roo.EventObject} e The event object
36835         */
36836        click : true
36837     });
36838     
36839 };
36840
36841 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36842
36843     radioes : false,
36844     
36845     inline : true,
36846     
36847     weight : '',
36848     
36849     indicatorpos : 'left',
36850     
36851     getAutoCreate : function()
36852     {
36853         var label = {
36854             tag : 'label',
36855             cls : 'roo-radio-set-label',
36856             cn : [
36857                 {
36858                     tag : 'span',
36859                     html : this.fieldLabel
36860                 }
36861             ]
36862         };
36863         if (Roo.bootstrap.version == 3) {
36864             
36865             
36866             if(this.indicatorpos == 'left'){
36867                 label.cn.unshift({
36868                     tag : 'i',
36869                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36870                     tooltip : 'This field is required'
36871                 });
36872             } else {
36873                 label.cn.push({
36874                     tag : 'i',
36875                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36876                     tooltip : 'This field is required'
36877                 });
36878             }
36879         }
36880         var items = {
36881             tag : 'div',
36882             cls : 'roo-radio-set-items'
36883         };
36884         
36885         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36886         
36887         if (align === 'left' && this.fieldLabel.length) {
36888             
36889             items = {
36890                 cls : "roo-radio-set-right", 
36891                 cn: [
36892                     items
36893                 ]
36894             };
36895             
36896             if(this.labelWidth > 12){
36897                 label.style = "width: " + this.labelWidth + 'px';
36898             }
36899             
36900             if(this.labelWidth < 13 && this.labelmd == 0){
36901                 this.labelmd = this.labelWidth;
36902             }
36903             
36904             if(this.labellg > 0){
36905                 label.cls += ' col-lg-' + this.labellg;
36906                 items.cls += ' col-lg-' + (12 - this.labellg);
36907             }
36908             
36909             if(this.labelmd > 0){
36910                 label.cls += ' col-md-' + this.labelmd;
36911                 items.cls += ' col-md-' + (12 - this.labelmd);
36912             }
36913             
36914             if(this.labelsm > 0){
36915                 label.cls += ' col-sm-' + this.labelsm;
36916                 items.cls += ' col-sm-' + (12 - this.labelsm);
36917             }
36918             
36919             if(this.labelxs > 0){
36920                 label.cls += ' col-xs-' + this.labelxs;
36921                 items.cls += ' col-xs-' + (12 - this.labelxs);
36922             }
36923         }
36924         
36925         var cfg = {
36926             tag : 'div',
36927             cls : 'roo-radio-set',
36928             cn : [
36929                 {
36930                     tag : 'input',
36931                     cls : 'roo-radio-set-input',
36932                     type : 'hidden',
36933                     name : this.name,
36934                     value : this.value ? this.value :  ''
36935                 },
36936                 label,
36937                 items
36938             ]
36939         };
36940         
36941         if(this.weight.length){
36942             cfg.cls += ' roo-radio-' + this.weight;
36943         }
36944         
36945         if(this.inline) {
36946             cfg.cls += ' roo-radio-set-inline';
36947         }
36948         
36949         var settings=this;
36950         ['xs','sm','md','lg'].map(function(size){
36951             if (settings[size]) {
36952                 cfg.cls += ' col-' + size + '-' + settings[size];
36953             }
36954         });
36955         
36956         return cfg;
36957         
36958     },
36959
36960     initEvents : function()
36961     {
36962         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36963         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36964         
36965         if(!this.fieldLabel.length){
36966             this.labelEl.hide();
36967         }
36968         
36969         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36970         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36971         
36972         this.indicator = this.indicatorEl();
36973         
36974         if(this.indicator){
36975             this.indicator.addClass('invisible');
36976         }
36977         
36978         this.originalValue = this.getValue();
36979         
36980     },
36981     
36982     inputEl: function ()
36983     {
36984         return this.el.select('.roo-radio-set-input', true).first();
36985     },
36986     
36987     getChildContainer : function()
36988     {
36989         return this.itemsEl;
36990     },
36991     
36992     register : function(item)
36993     {
36994         this.radioes.push(item);
36995         
36996     },
36997     
36998     validate : function()
36999     {   
37000         if(this.getVisibilityEl().hasClass('hidden')){
37001             return true;
37002         }
37003         
37004         var valid = false;
37005         
37006         Roo.each(this.radioes, function(i){
37007             if(!i.checked){
37008                 return;
37009             }
37010             
37011             valid = true;
37012             return false;
37013         });
37014         
37015         if(this.allowBlank) {
37016             return true;
37017         }
37018         
37019         if(this.disabled || valid){
37020             this.markValid();
37021             return true;
37022         }
37023         
37024         this.markInvalid();
37025         return false;
37026         
37027     },
37028     
37029     markValid : function()
37030     {
37031         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37032             this.indicatorEl().removeClass('visible');
37033             this.indicatorEl().addClass('invisible');
37034         }
37035         
37036         
37037         if (Roo.bootstrap.version == 3) {
37038             this.el.removeClass([this.invalidClass, this.validClass]);
37039             this.el.addClass(this.validClass);
37040         } else {
37041             this.el.removeClass(['is-invalid','is-valid']);
37042             this.el.addClass(['is-valid']);
37043         }
37044         this.fireEvent('valid', this);
37045     },
37046     
37047     markInvalid : function(msg)
37048     {
37049         if(this.allowBlank || this.disabled){
37050             return;
37051         }
37052         
37053         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37054             this.indicatorEl().removeClass('invisible');
37055             this.indicatorEl().addClass('visible');
37056         }
37057         if (Roo.bootstrap.version == 3) {
37058             this.el.removeClass([this.invalidClass, this.validClass]);
37059             this.el.addClass(this.invalidClass);
37060         } else {
37061             this.el.removeClass(['is-invalid','is-valid']);
37062             this.el.addClass(['is-invalid']);
37063         }
37064         
37065         this.fireEvent('invalid', this, msg);
37066         
37067     },
37068     
37069     setValue : function(v, suppressEvent)
37070     {   
37071         if(this.value === v){
37072             return;
37073         }
37074         
37075         this.value = v;
37076         
37077         if(this.rendered){
37078             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37079         }
37080         
37081         Roo.each(this.radioes, function(i){
37082             i.checked = false;
37083             i.el.removeClass('checked');
37084         });
37085         
37086         Roo.each(this.radioes, function(i){
37087             
37088             if(i.value === v || i.value.toString() === v.toString()){
37089                 i.checked = true;
37090                 i.el.addClass('checked');
37091                 
37092                 if(suppressEvent !== true){
37093                     this.fireEvent('check', this, i);
37094                 }
37095                 
37096                 return false;
37097             }
37098             
37099         }, this);
37100         
37101         this.validate();
37102     },
37103     
37104     clearInvalid : function(){
37105         
37106         if(!this.el || this.preventMark){
37107             return;
37108         }
37109         
37110         this.el.removeClass([this.invalidClass]);
37111         
37112         this.fireEvent('valid', this);
37113     }
37114     
37115 });
37116
37117 Roo.apply(Roo.bootstrap.RadioSet, {
37118     
37119     groups: {},
37120     
37121     register : function(set)
37122     {
37123         this.groups[set.name] = set;
37124     },
37125     
37126     get: function(name) 
37127     {
37128         if (typeof(this.groups[name]) == 'undefined') {
37129             return false;
37130         }
37131         
37132         return this.groups[name] ;
37133     }
37134     
37135 });
37136 /*
37137  * Based on:
37138  * Ext JS Library 1.1.1
37139  * Copyright(c) 2006-2007, Ext JS, LLC.
37140  *
37141  * Originally Released Under LGPL - original licence link has changed is not relivant.
37142  *
37143  * Fork - LGPL
37144  * <script type="text/javascript">
37145  */
37146
37147
37148 /**
37149  * @class Roo.bootstrap.SplitBar
37150  * @extends Roo.util.Observable
37151  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37152  * <br><br>
37153  * Usage:
37154  * <pre><code>
37155 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37156                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37157 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37158 split.minSize = 100;
37159 split.maxSize = 600;
37160 split.animate = true;
37161 split.on('moved', splitterMoved);
37162 </code></pre>
37163  * @constructor
37164  * Create a new SplitBar
37165  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37166  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37167  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37168  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37169                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37170                         position of the SplitBar).
37171  */
37172 Roo.bootstrap.SplitBar = function(cfg){
37173     
37174     /** @private */
37175     
37176     //{
37177     //  dragElement : elm
37178     //  resizingElement: el,
37179         // optional..
37180     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37181     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37182         // existingProxy ???
37183     //}
37184     
37185     this.el = Roo.get(cfg.dragElement, true);
37186     this.el.dom.unselectable = "on";
37187     /** @private */
37188     this.resizingEl = Roo.get(cfg.resizingElement, true);
37189
37190     /**
37191      * @private
37192      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37193      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37194      * @type Number
37195      */
37196     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37197     
37198     /**
37199      * The minimum size of the resizing element. (Defaults to 0)
37200      * @type Number
37201      */
37202     this.minSize = 0;
37203     
37204     /**
37205      * The maximum size of the resizing element. (Defaults to 2000)
37206      * @type Number
37207      */
37208     this.maxSize = 2000;
37209     
37210     /**
37211      * Whether to animate the transition to the new size
37212      * @type Boolean
37213      */
37214     this.animate = false;
37215     
37216     /**
37217      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37218      * @type Boolean
37219      */
37220     this.useShim = false;
37221     
37222     /** @private */
37223     this.shim = null;
37224     
37225     if(!cfg.existingProxy){
37226         /** @private */
37227         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37228     }else{
37229         this.proxy = Roo.get(cfg.existingProxy).dom;
37230     }
37231     /** @private */
37232     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37233     
37234     /** @private */
37235     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37236     
37237     /** @private */
37238     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37239     
37240     /** @private */
37241     this.dragSpecs = {};
37242     
37243     /**
37244      * @private The adapter to use to positon and resize elements
37245      */
37246     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37247     this.adapter.init(this);
37248     
37249     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37250         /** @private */
37251         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37252         this.el.addClass("roo-splitbar-h");
37253     }else{
37254         /** @private */
37255         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37256         this.el.addClass("roo-splitbar-v");
37257     }
37258     
37259     this.addEvents({
37260         /**
37261          * @event resize
37262          * Fires when the splitter is moved (alias for {@link #event-moved})
37263          * @param {Roo.bootstrap.SplitBar} this
37264          * @param {Number} newSize the new width or height
37265          */
37266         "resize" : true,
37267         /**
37268          * @event moved
37269          * Fires when the splitter is moved
37270          * @param {Roo.bootstrap.SplitBar} this
37271          * @param {Number} newSize the new width or height
37272          */
37273         "moved" : true,
37274         /**
37275          * @event beforeresize
37276          * Fires before the splitter is dragged
37277          * @param {Roo.bootstrap.SplitBar} this
37278          */
37279         "beforeresize" : true,
37280
37281         "beforeapply" : true
37282     });
37283
37284     Roo.util.Observable.call(this);
37285 };
37286
37287 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37288     onStartProxyDrag : function(x, y){
37289         this.fireEvent("beforeresize", this);
37290         if(!this.overlay){
37291             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37292             o.unselectable();
37293             o.enableDisplayMode("block");
37294             // all splitbars share the same overlay
37295             Roo.bootstrap.SplitBar.prototype.overlay = o;
37296         }
37297         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37298         this.overlay.show();
37299         Roo.get(this.proxy).setDisplayed("block");
37300         var size = this.adapter.getElementSize(this);
37301         this.activeMinSize = this.getMinimumSize();;
37302         this.activeMaxSize = this.getMaximumSize();;
37303         var c1 = size - this.activeMinSize;
37304         var c2 = Math.max(this.activeMaxSize - size, 0);
37305         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37306             this.dd.resetConstraints();
37307             this.dd.setXConstraint(
37308                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37309                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37310             );
37311             this.dd.setYConstraint(0, 0);
37312         }else{
37313             this.dd.resetConstraints();
37314             this.dd.setXConstraint(0, 0);
37315             this.dd.setYConstraint(
37316                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37317                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37318             );
37319          }
37320         this.dragSpecs.startSize = size;
37321         this.dragSpecs.startPoint = [x, y];
37322         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37323     },
37324     
37325     /** 
37326      * @private Called after the drag operation by the DDProxy
37327      */
37328     onEndProxyDrag : function(e){
37329         Roo.get(this.proxy).setDisplayed(false);
37330         var endPoint = Roo.lib.Event.getXY(e);
37331         if(this.overlay){
37332             this.overlay.hide();
37333         }
37334         var newSize;
37335         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37336             newSize = this.dragSpecs.startSize + 
37337                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37338                     endPoint[0] - this.dragSpecs.startPoint[0] :
37339                     this.dragSpecs.startPoint[0] - endPoint[0]
37340                 );
37341         }else{
37342             newSize = this.dragSpecs.startSize + 
37343                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37344                     endPoint[1] - this.dragSpecs.startPoint[1] :
37345                     this.dragSpecs.startPoint[1] - endPoint[1]
37346                 );
37347         }
37348         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37349         if(newSize != this.dragSpecs.startSize){
37350             if(this.fireEvent('beforeapply', this, newSize) !== false){
37351                 this.adapter.setElementSize(this, newSize);
37352                 this.fireEvent("moved", this, newSize);
37353                 this.fireEvent("resize", this, newSize);
37354             }
37355         }
37356     },
37357     
37358     /**
37359      * Get the adapter this SplitBar uses
37360      * @return The adapter object
37361      */
37362     getAdapter : function(){
37363         return this.adapter;
37364     },
37365     
37366     /**
37367      * Set the adapter this SplitBar uses
37368      * @param {Object} adapter A SplitBar adapter object
37369      */
37370     setAdapter : function(adapter){
37371         this.adapter = adapter;
37372         this.adapter.init(this);
37373     },
37374     
37375     /**
37376      * Gets the minimum size for the resizing element
37377      * @return {Number} The minimum size
37378      */
37379     getMinimumSize : function(){
37380         return this.minSize;
37381     },
37382     
37383     /**
37384      * Sets the minimum size for the resizing element
37385      * @param {Number} minSize The minimum size
37386      */
37387     setMinimumSize : function(minSize){
37388         this.minSize = minSize;
37389     },
37390     
37391     /**
37392      * Gets the maximum size for the resizing element
37393      * @return {Number} The maximum size
37394      */
37395     getMaximumSize : function(){
37396         return this.maxSize;
37397     },
37398     
37399     /**
37400      * Sets the maximum size for the resizing element
37401      * @param {Number} maxSize The maximum size
37402      */
37403     setMaximumSize : function(maxSize){
37404         this.maxSize = maxSize;
37405     },
37406     
37407     /**
37408      * Sets the initialize size for the resizing element
37409      * @param {Number} size The initial size
37410      */
37411     setCurrentSize : function(size){
37412         var oldAnimate = this.animate;
37413         this.animate = false;
37414         this.adapter.setElementSize(this, size);
37415         this.animate = oldAnimate;
37416     },
37417     
37418     /**
37419      * Destroy this splitbar. 
37420      * @param {Boolean} removeEl True to remove the element
37421      */
37422     destroy : function(removeEl){
37423         if(this.shim){
37424             this.shim.remove();
37425         }
37426         this.dd.unreg();
37427         this.proxy.parentNode.removeChild(this.proxy);
37428         if(removeEl){
37429             this.el.remove();
37430         }
37431     }
37432 });
37433
37434 /**
37435  * @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.
37436  */
37437 Roo.bootstrap.SplitBar.createProxy = function(dir){
37438     var proxy = new Roo.Element(document.createElement("div"));
37439     proxy.unselectable();
37440     var cls = 'roo-splitbar-proxy';
37441     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37442     document.body.appendChild(proxy.dom);
37443     return proxy.dom;
37444 };
37445
37446 /** 
37447  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37448  * Default Adapter. It assumes the splitter and resizing element are not positioned
37449  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37450  */
37451 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37452 };
37453
37454 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37455     // do nothing for now
37456     init : function(s){
37457     
37458     },
37459     /**
37460      * Called before drag operations to get the current size of the resizing element. 
37461      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37462      */
37463      getElementSize : function(s){
37464         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37465             return s.resizingEl.getWidth();
37466         }else{
37467             return s.resizingEl.getHeight();
37468         }
37469     },
37470     
37471     /**
37472      * Called after drag operations to set the size of the resizing element.
37473      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37474      * @param {Number} newSize The new size to set
37475      * @param {Function} onComplete A function to be invoked when resizing is complete
37476      */
37477     setElementSize : function(s, newSize, onComplete){
37478         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37479             if(!s.animate){
37480                 s.resizingEl.setWidth(newSize);
37481                 if(onComplete){
37482                     onComplete(s, newSize);
37483                 }
37484             }else{
37485                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37486             }
37487         }else{
37488             
37489             if(!s.animate){
37490                 s.resizingEl.setHeight(newSize);
37491                 if(onComplete){
37492                     onComplete(s, newSize);
37493                 }
37494             }else{
37495                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37496             }
37497         }
37498     }
37499 };
37500
37501 /** 
37502  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37503  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37504  * Adapter that  moves the splitter element to align with the resized sizing element. 
37505  * Used with an absolute positioned SplitBar.
37506  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37507  * document.body, make sure you assign an id to the body element.
37508  */
37509 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37510     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37511     this.container = Roo.get(container);
37512 };
37513
37514 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37515     init : function(s){
37516         this.basic.init(s);
37517     },
37518     
37519     getElementSize : function(s){
37520         return this.basic.getElementSize(s);
37521     },
37522     
37523     setElementSize : function(s, newSize, onComplete){
37524         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37525     },
37526     
37527     moveSplitter : function(s){
37528         var yes = Roo.bootstrap.SplitBar;
37529         switch(s.placement){
37530             case yes.LEFT:
37531                 s.el.setX(s.resizingEl.getRight());
37532                 break;
37533             case yes.RIGHT:
37534                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37535                 break;
37536             case yes.TOP:
37537                 s.el.setY(s.resizingEl.getBottom());
37538                 break;
37539             case yes.BOTTOM:
37540                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37541                 break;
37542         }
37543     }
37544 };
37545
37546 /**
37547  * Orientation constant - Create a vertical SplitBar
37548  * @static
37549  * @type Number
37550  */
37551 Roo.bootstrap.SplitBar.VERTICAL = 1;
37552
37553 /**
37554  * Orientation constant - Create a horizontal SplitBar
37555  * @static
37556  * @type Number
37557  */
37558 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37559
37560 /**
37561  * Placement constant - The resizing element is to the left of the splitter element
37562  * @static
37563  * @type Number
37564  */
37565 Roo.bootstrap.SplitBar.LEFT = 1;
37566
37567 /**
37568  * Placement constant - The resizing element is to the right of the splitter element
37569  * @static
37570  * @type Number
37571  */
37572 Roo.bootstrap.SplitBar.RIGHT = 2;
37573
37574 /**
37575  * Placement constant - The resizing element is positioned above the splitter element
37576  * @static
37577  * @type Number
37578  */
37579 Roo.bootstrap.SplitBar.TOP = 3;
37580
37581 /**
37582  * Placement constant - The resizing element is positioned under splitter element
37583  * @static
37584  * @type Number
37585  */
37586 Roo.bootstrap.SplitBar.BOTTOM = 4;
37587 Roo.namespace("Roo.bootstrap.layout");/*
37588  * Based on:
37589  * Ext JS Library 1.1.1
37590  * Copyright(c) 2006-2007, Ext JS, LLC.
37591  *
37592  * Originally Released Under LGPL - original licence link has changed is not relivant.
37593  *
37594  * Fork - LGPL
37595  * <script type="text/javascript">
37596  */
37597
37598 /**
37599  * @class Roo.bootstrap.layout.Manager
37600  * @extends Roo.bootstrap.Component
37601  * Base class for layout managers.
37602  */
37603 Roo.bootstrap.layout.Manager = function(config)
37604 {
37605     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37606
37607
37608
37609
37610
37611     /** false to disable window resize monitoring @type Boolean */
37612     this.monitorWindowResize = true;
37613     this.regions = {};
37614     this.addEvents({
37615         /**
37616          * @event layout
37617          * Fires when a layout is performed.
37618          * @param {Roo.LayoutManager} this
37619          */
37620         "layout" : true,
37621         /**
37622          * @event regionresized
37623          * Fires when the user resizes a region.
37624          * @param {Roo.LayoutRegion} region The resized region
37625          * @param {Number} newSize The new size (width for east/west, height for north/south)
37626          */
37627         "regionresized" : true,
37628         /**
37629          * @event regioncollapsed
37630          * Fires when a region is collapsed.
37631          * @param {Roo.LayoutRegion} region The collapsed region
37632          */
37633         "regioncollapsed" : true,
37634         /**
37635          * @event regionexpanded
37636          * Fires when a region is expanded.
37637          * @param {Roo.LayoutRegion} region The expanded region
37638          */
37639         "regionexpanded" : true
37640     });
37641     this.updating = false;
37642
37643     if (config.el) {
37644         this.el = Roo.get(config.el);
37645         this.initEvents();
37646     }
37647
37648 };
37649
37650 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37651
37652
37653     regions : null,
37654
37655     monitorWindowResize : true,
37656
37657
37658     updating : false,
37659
37660
37661     onRender : function(ct, position)
37662     {
37663         if(!this.el){
37664             this.el = Roo.get(ct);
37665             this.initEvents();
37666         }
37667         //this.fireEvent('render',this);
37668     },
37669
37670
37671     initEvents: function()
37672     {
37673
37674
37675         // ie scrollbar fix
37676         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37677             document.body.scroll = "no";
37678         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37679             this.el.position('relative');
37680         }
37681         this.id = this.el.id;
37682         this.el.addClass("roo-layout-container");
37683         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37684         if(this.el.dom != document.body ) {
37685             this.el.on('resize', this.layout,this);
37686             this.el.on('show', this.layout,this);
37687         }
37688
37689     },
37690
37691     /**
37692      * Returns true if this layout is currently being updated
37693      * @return {Boolean}
37694      */
37695     isUpdating : function(){
37696         return this.updating;
37697     },
37698
37699     /**
37700      * Suspend the LayoutManager from doing auto-layouts while
37701      * making multiple add or remove calls
37702      */
37703     beginUpdate : function(){
37704         this.updating = true;
37705     },
37706
37707     /**
37708      * Restore auto-layouts and optionally disable the manager from performing a layout
37709      * @param {Boolean} noLayout true to disable a layout update
37710      */
37711     endUpdate : function(noLayout){
37712         this.updating = false;
37713         if(!noLayout){
37714             this.layout();
37715         }
37716     },
37717
37718     layout: function(){
37719         // abstract...
37720     },
37721
37722     onRegionResized : function(region, newSize){
37723         this.fireEvent("regionresized", region, newSize);
37724         this.layout();
37725     },
37726
37727     onRegionCollapsed : function(region){
37728         this.fireEvent("regioncollapsed", region);
37729     },
37730
37731     onRegionExpanded : function(region){
37732         this.fireEvent("regionexpanded", region);
37733     },
37734
37735     /**
37736      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37737      * performs box-model adjustments.
37738      * @return {Object} The size as an object {width: (the width), height: (the height)}
37739      */
37740     getViewSize : function()
37741     {
37742         var size;
37743         if(this.el.dom != document.body){
37744             size = this.el.getSize();
37745         }else{
37746             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37747         }
37748         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37749         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37750         return size;
37751     },
37752
37753     /**
37754      * Returns the Element this layout is bound to.
37755      * @return {Roo.Element}
37756      */
37757     getEl : function(){
37758         return this.el;
37759     },
37760
37761     /**
37762      * Returns the specified region.
37763      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37764      * @return {Roo.LayoutRegion}
37765      */
37766     getRegion : function(target){
37767         return this.regions[target.toLowerCase()];
37768     },
37769
37770     onWindowResize : function(){
37771         if(this.monitorWindowResize){
37772             this.layout();
37773         }
37774     }
37775 });
37776 /*
37777  * Based on:
37778  * Ext JS Library 1.1.1
37779  * Copyright(c) 2006-2007, Ext JS, LLC.
37780  *
37781  * Originally Released Under LGPL - original licence link has changed is not relivant.
37782  *
37783  * Fork - LGPL
37784  * <script type="text/javascript">
37785  */
37786 /**
37787  * @class Roo.bootstrap.layout.Border
37788  * @extends Roo.bootstrap.layout.Manager
37789  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37790  * please see: examples/bootstrap/nested.html<br><br>
37791  
37792 <b>The container the layout is rendered into can be either the body element or any other element.
37793 If it is not the body element, the container needs to either be an absolute positioned element,
37794 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37795 the container size if it is not the body element.</b>
37796
37797 * @constructor
37798 * Create a new Border
37799 * @param {Object} config Configuration options
37800  */
37801 Roo.bootstrap.layout.Border = function(config){
37802     config = config || {};
37803     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37804     
37805     
37806     
37807     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37808         if(config[region]){
37809             config[region].region = region;
37810             this.addRegion(config[region]);
37811         }
37812     },this);
37813     
37814 };
37815
37816 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37817
37818 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37819     
37820     parent : false, // this might point to a 'nest' or a ???
37821     
37822     /**
37823      * Creates and adds a new region if it doesn't already exist.
37824      * @param {String} target The target region key (north, south, east, west or center).
37825      * @param {Object} config The regions config object
37826      * @return {BorderLayoutRegion} The new region
37827      */
37828     addRegion : function(config)
37829     {
37830         if(!this.regions[config.region]){
37831             var r = this.factory(config);
37832             this.bindRegion(r);
37833         }
37834         return this.regions[config.region];
37835     },
37836
37837     // private (kinda)
37838     bindRegion : function(r){
37839         this.regions[r.config.region] = r;
37840         
37841         r.on("visibilitychange",    this.layout, this);
37842         r.on("paneladded",          this.layout, this);
37843         r.on("panelremoved",        this.layout, this);
37844         r.on("invalidated",         this.layout, this);
37845         r.on("resized",             this.onRegionResized, this);
37846         r.on("collapsed",           this.onRegionCollapsed, this);
37847         r.on("expanded",            this.onRegionExpanded, this);
37848     },
37849
37850     /**
37851      * Performs a layout update.
37852      */
37853     layout : function()
37854     {
37855         if(this.updating) {
37856             return;
37857         }
37858         
37859         // render all the rebions if they have not been done alreayd?
37860         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37861             if(this.regions[region] && !this.regions[region].bodyEl){
37862                 this.regions[region].onRender(this.el)
37863             }
37864         },this);
37865         
37866         var size = this.getViewSize();
37867         var w = size.width;
37868         var h = size.height;
37869         var centerW = w;
37870         var centerH = h;
37871         var centerY = 0;
37872         var centerX = 0;
37873         //var x = 0, y = 0;
37874
37875         var rs = this.regions;
37876         var north = rs["north"];
37877         var south = rs["south"]; 
37878         var west = rs["west"];
37879         var east = rs["east"];
37880         var center = rs["center"];
37881         //if(this.hideOnLayout){ // not supported anymore
37882             //c.el.setStyle("display", "none");
37883         //}
37884         if(north && north.isVisible()){
37885             var b = north.getBox();
37886             var m = north.getMargins();
37887             b.width = w - (m.left+m.right);
37888             b.x = m.left;
37889             b.y = m.top;
37890             centerY = b.height + b.y + m.bottom;
37891             centerH -= centerY;
37892             north.updateBox(this.safeBox(b));
37893         }
37894         if(south && south.isVisible()){
37895             var b = south.getBox();
37896             var m = south.getMargins();
37897             b.width = w - (m.left+m.right);
37898             b.x = m.left;
37899             var totalHeight = (b.height + m.top + m.bottom);
37900             b.y = h - totalHeight + m.top;
37901             centerH -= totalHeight;
37902             south.updateBox(this.safeBox(b));
37903         }
37904         if(west && west.isVisible()){
37905             var b = west.getBox();
37906             var m = west.getMargins();
37907             b.height = centerH - (m.top+m.bottom);
37908             b.x = m.left;
37909             b.y = centerY + m.top;
37910             var totalWidth = (b.width + m.left + m.right);
37911             centerX += totalWidth;
37912             centerW -= totalWidth;
37913             west.updateBox(this.safeBox(b));
37914         }
37915         if(east && east.isVisible()){
37916             var b = east.getBox();
37917             var m = east.getMargins();
37918             b.height = centerH - (m.top+m.bottom);
37919             var totalWidth = (b.width + m.left + m.right);
37920             b.x = w - totalWidth + m.left;
37921             b.y = centerY + m.top;
37922             centerW -= totalWidth;
37923             east.updateBox(this.safeBox(b));
37924         }
37925         if(center){
37926             var m = center.getMargins();
37927             var centerBox = {
37928                 x: centerX + m.left,
37929                 y: centerY + m.top,
37930                 width: centerW - (m.left+m.right),
37931                 height: centerH - (m.top+m.bottom)
37932             };
37933             //if(this.hideOnLayout){
37934                 //center.el.setStyle("display", "block");
37935             //}
37936             center.updateBox(this.safeBox(centerBox));
37937         }
37938         this.el.repaint();
37939         this.fireEvent("layout", this);
37940     },
37941
37942     // private
37943     safeBox : function(box){
37944         box.width = Math.max(0, box.width);
37945         box.height = Math.max(0, box.height);
37946         return box;
37947     },
37948
37949     /**
37950      * Adds a ContentPanel (or subclass) to this layout.
37951      * @param {String} target The target region key (north, south, east, west or center).
37952      * @param {Roo.ContentPanel} panel The panel to add
37953      * @return {Roo.ContentPanel} The added panel
37954      */
37955     add : function(target, panel){
37956          
37957         target = target.toLowerCase();
37958         return this.regions[target].add(panel);
37959     },
37960
37961     /**
37962      * Remove a ContentPanel (or subclass) to this layout.
37963      * @param {String} target The target region key (north, south, east, west or center).
37964      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37965      * @return {Roo.ContentPanel} The removed panel
37966      */
37967     remove : function(target, panel){
37968         target = target.toLowerCase();
37969         return this.regions[target].remove(panel);
37970     },
37971
37972     /**
37973      * Searches all regions for a panel with the specified id
37974      * @param {String} panelId
37975      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37976      */
37977     findPanel : function(panelId){
37978         var rs = this.regions;
37979         for(var target in rs){
37980             if(typeof rs[target] != "function"){
37981                 var p = rs[target].getPanel(panelId);
37982                 if(p){
37983                     return p;
37984                 }
37985             }
37986         }
37987         return null;
37988     },
37989
37990     /**
37991      * Searches all regions for a panel with the specified id and activates (shows) it.
37992      * @param {String/ContentPanel} panelId The panels id or the panel itself
37993      * @return {Roo.ContentPanel} The shown panel or null
37994      */
37995     showPanel : function(panelId) {
37996       var rs = this.regions;
37997       for(var target in rs){
37998          var r = rs[target];
37999          if(typeof r != "function"){
38000             if(r.hasPanel(panelId)){
38001                return r.showPanel(panelId);
38002             }
38003          }
38004       }
38005       return null;
38006    },
38007
38008    /**
38009      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38010      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38011      */
38012    /*
38013     restoreState : function(provider){
38014         if(!provider){
38015             provider = Roo.state.Manager;
38016         }
38017         var sm = new Roo.LayoutStateManager();
38018         sm.init(this, provider);
38019     },
38020 */
38021  
38022  
38023     /**
38024      * Adds a xtype elements to the layout.
38025      * <pre><code>
38026
38027 layout.addxtype({
38028        xtype : 'ContentPanel',
38029        region: 'west',
38030        items: [ .... ]
38031    }
38032 );
38033
38034 layout.addxtype({
38035         xtype : 'NestedLayoutPanel',
38036         region: 'west',
38037         layout: {
38038            center: { },
38039            west: { }   
38040         },
38041         items : [ ... list of content panels or nested layout panels.. ]
38042    }
38043 );
38044 </code></pre>
38045      * @param {Object} cfg Xtype definition of item to add.
38046      */
38047     addxtype : function(cfg)
38048     {
38049         // basically accepts a pannel...
38050         // can accept a layout region..!?!?
38051         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38052         
38053         
38054         // theory?  children can only be panels??
38055         
38056         //if (!cfg.xtype.match(/Panel$/)) {
38057         //    return false;
38058         //}
38059         var ret = false;
38060         
38061         if (typeof(cfg.region) == 'undefined') {
38062             Roo.log("Failed to add Panel, region was not set");
38063             Roo.log(cfg);
38064             return false;
38065         }
38066         var region = cfg.region;
38067         delete cfg.region;
38068         
38069           
38070         var xitems = [];
38071         if (cfg.items) {
38072             xitems = cfg.items;
38073             delete cfg.items;
38074         }
38075         var nb = false;
38076         
38077         if ( region == 'center') {
38078             Roo.log("Center: " + cfg.title);
38079         }
38080         
38081         
38082         switch(cfg.xtype) 
38083         {
38084             case 'Content':  // ContentPanel (el, cfg)
38085             case 'Scroll':  // ContentPanel (el, cfg)
38086             case 'View': 
38087                 cfg.autoCreate = cfg.autoCreate || true;
38088                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38089                 //} else {
38090                 //    var el = this.el.createChild();
38091                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38092                 //}
38093                 
38094                 this.add(region, ret);
38095                 break;
38096             
38097             /*
38098             case 'TreePanel': // our new panel!
38099                 cfg.el = this.el.createChild();
38100                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38101                 this.add(region, ret);
38102                 break;
38103             */
38104             
38105             case 'Nest': 
38106                 // create a new Layout (which is  a Border Layout...
38107                 
38108                 var clayout = cfg.layout;
38109                 clayout.el  = this.el.createChild();
38110                 clayout.items   = clayout.items  || [];
38111                 
38112                 delete cfg.layout;
38113                 
38114                 // replace this exitems with the clayout ones..
38115                 xitems = clayout.items;
38116                  
38117                 // force background off if it's in center...
38118                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38119                     cfg.background = false;
38120                 }
38121                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38122                 
38123                 
38124                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38125                 //console.log('adding nested layout panel '  + cfg.toSource());
38126                 this.add(region, ret);
38127                 nb = {}; /// find first...
38128                 break;
38129             
38130             case 'Grid':
38131                 
38132                 // needs grid and region
38133                 
38134                 //var el = this.getRegion(region).el.createChild();
38135                 /*
38136                  *var el = this.el.createChild();
38137                 // create the grid first...
38138                 cfg.grid.container = el;
38139                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38140                 */
38141                 
38142                 if (region == 'center' && this.active ) {
38143                     cfg.background = false;
38144                 }
38145                 
38146                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38147                 
38148                 this.add(region, ret);
38149                 /*
38150                 if (cfg.background) {
38151                     // render grid on panel activation (if panel background)
38152                     ret.on('activate', function(gp) {
38153                         if (!gp.grid.rendered) {
38154                     //        gp.grid.render(el);
38155                         }
38156                     });
38157                 } else {
38158                   //  cfg.grid.render(el);
38159                 }
38160                 */
38161                 break;
38162            
38163            
38164             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38165                 // it was the old xcomponent building that caused this before.
38166                 // espeically if border is the top element in the tree.
38167                 ret = this;
38168                 break; 
38169                 
38170                     
38171                 
38172                 
38173                 
38174             default:
38175                 /*
38176                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38177                     
38178                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38179                     this.add(region, ret);
38180                 } else {
38181                 */
38182                     Roo.log(cfg);
38183                     throw "Can not add '" + cfg.xtype + "' to Border";
38184                     return null;
38185              
38186                                 
38187              
38188         }
38189         this.beginUpdate();
38190         // add children..
38191         var region = '';
38192         var abn = {};
38193         Roo.each(xitems, function(i)  {
38194             region = nb && i.region ? i.region : false;
38195             
38196             var add = ret.addxtype(i);
38197            
38198             if (region) {
38199                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38200                 if (!i.background) {
38201                     abn[region] = nb[region] ;
38202                 }
38203             }
38204             
38205         });
38206         this.endUpdate();
38207
38208         // make the last non-background panel active..
38209         //if (nb) { Roo.log(abn); }
38210         if (nb) {
38211             
38212             for(var r in abn) {
38213                 region = this.getRegion(r);
38214                 if (region) {
38215                     // tried using nb[r], but it does not work..
38216                      
38217                     region.showPanel(abn[r]);
38218                    
38219                 }
38220             }
38221         }
38222         return ret;
38223         
38224     },
38225     
38226     
38227 // private
38228     factory : function(cfg)
38229     {
38230         
38231         var validRegions = Roo.bootstrap.layout.Border.regions;
38232
38233         var target = cfg.region;
38234         cfg.mgr = this;
38235         
38236         var r = Roo.bootstrap.layout;
38237         Roo.log(target);
38238         switch(target){
38239             case "north":
38240                 return new r.North(cfg);
38241             case "south":
38242                 return new r.South(cfg);
38243             case "east":
38244                 return new r.East(cfg);
38245             case "west":
38246                 return new r.West(cfg);
38247             case "center":
38248                 return new r.Center(cfg);
38249         }
38250         throw 'Layout region "'+target+'" not supported.';
38251     }
38252     
38253     
38254 });
38255  /*
38256  * Based on:
38257  * Ext JS Library 1.1.1
38258  * Copyright(c) 2006-2007, Ext JS, LLC.
38259  *
38260  * Originally Released Under LGPL - original licence link has changed is not relivant.
38261  *
38262  * Fork - LGPL
38263  * <script type="text/javascript">
38264  */
38265  
38266 /**
38267  * @class Roo.bootstrap.layout.Basic
38268  * @extends Roo.util.Observable
38269  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38270  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38271  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38272  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38273  * @cfg {string}   region  the region that it inhabits..
38274  * @cfg {bool}   skipConfig skip config?
38275  * 
38276
38277  */
38278 Roo.bootstrap.layout.Basic = function(config){
38279     
38280     this.mgr = config.mgr;
38281     
38282     this.position = config.region;
38283     
38284     var skipConfig = config.skipConfig;
38285     
38286     this.events = {
38287         /**
38288          * @scope Roo.BasicLayoutRegion
38289          */
38290         
38291         /**
38292          * @event beforeremove
38293          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38294          * @param {Roo.LayoutRegion} this
38295          * @param {Roo.ContentPanel} panel The panel
38296          * @param {Object} e The cancel event object
38297          */
38298         "beforeremove" : true,
38299         /**
38300          * @event invalidated
38301          * Fires when the layout for this region is changed.
38302          * @param {Roo.LayoutRegion} this
38303          */
38304         "invalidated" : true,
38305         /**
38306          * @event visibilitychange
38307          * Fires when this region is shown or hidden 
38308          * @param {Roo.LayoutRegion} this
38309          * @param {Boolean} visibility true or false
38310          */
38311         "visibilitychange" : true,
38312         /**
38313          * @event paneladded
38314          * Fires when a panel is added. 
38315          * @param {Roo.LayoutRegion} this
38316          * @param {Roo.ContentPanel} panel The panel
38317          */
38318         "paneladded" : true,
38319         /**
38320          * @event panelremoved
38321          * Fires when a panel is removed. 
38322          * @param {Roo.LayoutRegion} this
38323          * @param {Roo.ContentPanel} panel The panel
38324          */
38325         "panelremoved" : true,
38326         /**
38327          * @event beforecollapse
38328          * Fires when this region before collapse.
38329          * @param {Roo.LayoutRegion} this
38330          */
38331         "beforecollapse" : true,
38332         /**
38333          * @event collapsed
38334          * Fires when this region is collapsed.
38335          * @param {Roo.LayoutRegion} this
38336          */
38337         "collapsed" : true,
38338         /**
38339          * @event expanded
38340          * Fires when this region is expanded.
38341          * @param {Roo.LayoutRegion} this
38342          */
38343         "expanded" : true,
38344         /**
38345          * @event slideshow
38346          * Fires when this region is slid into view.
38347          * @param {Roo.LayoutRegion} this
38348          */
38349         "slideshow" : true,
38350         /**
38351          * @event slidehide
38352          * Fires when this region slides out of view. 
38353          * @param {Roo.LayoutRegion} this
38354          */
38355         "slidehide" : true,
38356         /**
38357          * @event panelactivated
38358          * Fires when a panel is activated. 
38359          * @param {Roo.LayoutRegion} this
38360          * @param {Roo.ContentPanel} panel The activated panel
38361          */
38362         "panelactivated" : true,
38363         /**
38364          * @event resized
38365          * Fires when the user resizes this region. 
38366          * @param {Roo.LayoutRegion} this
38367          * @param {Number} newSize The new size (width for east/west, height for north/south)
38368          */
38369         "resized" : true
38370     };
38371     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38372     this.panels = new Roo.util.MixedCollection();
38373     this.panels.getKey = this.getPanelId.createDelegate(this);
38374     this.box = null;
38375     this.activePanel = null;
38376     // ensure listeners are added...
38377     
38378     if (config.listeners || config.events) {
38379         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38380             listeners : config.listeners || {},
38381             events : config.events || {}
38382         });
38383     }
38384     
38385     if(skipConfig !== true){
38386         this.applyConfig(config);
38387     }
38388 };
38389
38390 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38391 {
38392     getPanelId : function(p){
38393         return p.getId();
38394     },
38395     
38396     applyConfig : function(config){
38397         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38398         this.config = config;
38399         
38400     },
38401     
38402     /**
38403      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38404      * the width, for horizontal (north, south) the height.
38405      * @param {Number} newSize The new width or height
38406      */
38407     resizeTo : function(newSize){
38408         var el = this.el ? this.el :
38409                  (this.activePanel ? this.activePanel.getEl() : null);
38410         if(el){
38411             switch(this.position){
38412                 case "east":
38413                 case "west":
38414                     el.setWidth(newSize);
38415                     this.fireEvent("resized", this, newSize);
38416                 break;
38417                 case "north":
38418                 case "south":
38419                     el.setHeight(newSize);
38420                     this.fireEvent("resized", this, newSize);
38421                 break;                
38422             }
38423         }
38424     },
38425     
38426     getBox : function(){
38427         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38428     },
38429     
38430     getMargins : function(){
38431         return this.margins;
38432     },
38433     
38434     updateBox : function(box){
38435         this.box = box;
38436         var el = this.activePanel.getEl();
38437         el.dom.style.left = box.x + "px";
38438         el.dom.style.top = box.y + "px";
38439         this.activePanel.setSize(box.width, box.height);
38440     },
38441     
38442     /**
38443      * Returns the container element for this region.
38444      * @return {Roo.Element}
38445      */
38446     getEl : function(){
38447         return this.activePanel;
38448     },
38449     
38450     /**
38451      * Returns true if this region is currently visible.
38452      * @return {Boolean}
38453      */
38454     isVisible : function(){
38455         return this.activePanel ? true : false;
38456     },
38457     
38458     setActivePanel : function(panel){
38459         panel = this.getPanel(panel);
38460         if(this.activePanel && this.activePanel != panel){
38461             this.activePanel.setActiveState(false);
38462             this.activePanel.getEl().setLeftTop(-10000,-10000);
38463         }
38464         this.activePanel = panel;
38465         panel.setActiveState(true);
38466         if(this.box){
38467             panel.setSize(this.box.width, this.box.height);
38468         }
38469         this.fireEvent("panelactivated", this, panel);
38470         this.fireEvent("invalidated");
38471     },
38472     
38473     /**
38474      * Show the specified panel.
38475      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38476      * @return {Roo.ContentPanel} The shown panel or null
38477      */
38478     showPanel : function(panel){
38479         panel = this.getPanel(panel);
38480         if(panel){
38481             this.setActivePanel(panel);
38482         }
38483         return panel;
38484     },
38485     
38486     /**
38487      * Get the active panel for this region.
38488      * @return {Roo.ContentPanel} The active panel or null
38489      */
38490     getActivePanel : function(){
38491         return this.activePanel;
38492     },
38493     
38494     /**
38495      * Add the passed ContentPanel(s)
38496      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38497      * @return {Roo.ContentPanel} The panel added (if only one was added)
38498      */
38499     add : function(panel){
38500         if(arguments.length > 1){
38501             for(var i = 0, len = arguments.length; i < len; i++) {
38502                 this.add(arguments[i]);
38503             }
38504             return null;
38505         }
38506         if(this.hasPanel(panel)){
38507             this.showPanel(panel);
38508             return panel;
38509         }
38510         var el = panel.getEl();
38511         if(el.dom.parentNode != this.mgr.el.dom){
38512             this.mgr.el.dom.appendChild(el.dom);
38513         }
38514         if(panel.setRegion){
38515             panel.setRegion(this);
38516         }
38517         this.panels.add(panel);
38518         el.setStyle("position", "absolute");
38519         if(!panel.background){
38520             this.setActivePanel(panel);
38521             if(this.config.initialSize && this.panels.getCount()==1){
38522                 this.resizeTo(this.config.initialSize);
38523             }
38524         }
38525         this.fireEvent("paneladded", this, panel);
38526         return panel;
38527     },
38528     
38529     /**
38530      * Returns true if the panel is in this region.
38531      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38532      * @return {Boolean}
38533      */
38534     hasPanel : function(panel){
38535         if(typeof panel == "object"){ // must be panel obj
38536             panel = panel.getId();
38537         }
38538         return this.getPanel(panel) ? true : false;
38539     },
38540     
38541     /**
38542      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38543      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38544      * @param {Boolean} preservePanel Overrides the config preservePanel option
38545      * @return {Roo.ContentPanel} The panel that was removed
38546      */
38547     remove : function(panel, preservePanel){
38548         panel = this.getPanel(panel);
38549         if(!panel){
38550             return null;
38551         }
38552         var e = {};
38553         this.fireEvent("beforeremove", this, panel, e);
38554         if(e.cancel === true){
38555             return null;
38556         }
38557         var panelId = panel.getId();
38558         this.panels.removeKey(panelId);
38559         return panel;
38560     },
38561     
38562     /**
38563      * Returns the panel specified or null if it's not in this region.
38564      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38565      * @return {Roo.ContentPanel}
38566      */
38567     getPanel : function(id){
38568         if(typeof id == "object"){ // must be panel obj
38569             return id;
38570         }
38571         return this.panels.get(id);
38572     },
38573     
38574     /**
38575      * Returns this regions position (north/south/east/west/center).
38576      * @return {String} 
38577      */
38578     getPosition: function(){
38579         return this.position;    
38580     }
38581 });/*
38582  * Based on:
38583  * Ext JS Library 1.1.1
38584  * Copyright(c) 2006-2007, Ext JS, LLC.
38585  *
38586  * Originally Released Under LGPL - original licence link has changed is not relivant.
38587  *
38588  * Fork - LGPL
38589  * <script type="text/javascript">
38590  */
38591  
38592 /**
38593  * @class Roo.bootstrap.layout.Region
38594  * @extends Roo.bootstrap.layout.Basic
38595  * This class represents a region in a layout manager.
38596  
38597  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38598  * @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})
38599  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38600  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38601  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38602  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38603  * @cfg {String}    title           The title for the region (overrides panel titles)
38604  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38605  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38606  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38607  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38608  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38609  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38610  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38611  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38612  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38613  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38614
38615  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38616  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38617  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38618  * @cfg {Number}    width           For East/West panels
38619  * @cfg {Number}    height          For North/South panels
38620  * @cfg {Boolean}   split           To show the splitter
38621  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38622  * 
38623  * @cfg {string}   cls             Extra CSS classes to add to region
38624  * 
38625  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38626  * @cfg {string}   region  the region that it inhabits..
38627  *
38628
38629  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38630  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38631
38632  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38633  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38634  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38635  */
38636 Roo.bootstrap.layout.Region = function(config)
38637 {
38638     this.applyConfig(config);
38639
38640     var mgr = config.mgr;
38641     var pos = config.region;
38642     config.skipConfig = true;
38643     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38644     
38645     if (mgr.el) {
38646         this.onRender(mgr.el);   
38647     }
38648      
38649     this.visible = true;
38650     this.collapsed = false;
38651     this.unrendered_panels = [];
38652 };
38653
38654 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38655
38656     position: '', // set by wrapper (eg. north/south etc..)
38657     unrendered_panels : null,  // unrendered panels.
38658     
38659     tabPosition : false,
38660     
38661     mgr: false, // points to 'Border'
38662     
38663     
38664     createBody : function(){
38665         /** This region's body element 
38666         * @type Roo.Element */
38667         this.bodyEl = this.el.createChild({
38668                 tag: "div",
38669                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38670         });
38671     },
38672
38673     onRender: function(ctr, pos)
38674     {
38675         var dh = Roo.DomHelper;
38676         /** This region's container element 
38677         * @type Roo.Element */
38678         this.el = dh.append(ctr.dom, {
38679                 tag: "div",
38680                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38681             }, true);
38682         /** This region's title element 
38683         * @type Roo.Element */
38684     
38685         this.titleEl = dh.append(this.el.dom,  {
38686                 tag: "div",
38687                 unselectable: "on",
38688                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38689                 children:[
38690                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38691                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38692                 ]
38693             }, true);
38694         
38695         this.titleEl.enableDisplayMode();
38696         /** This region's title text element 
38697         * @type HTMLElement */
38698         this.titleTextEl = this.titleEl.dom.firstChild;
38699         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38700         /*
38701         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38702         this.closeBtn.enableDisplayMode();
38703         this.closeBtn.on("click", this.closeClicked, this);
38704         this.closeBtn.hide();
38705     */
38706         this.createBody(this.config);
38707         if(this.config.hideWhenEmpty){
38708             this.hide();
38709             this.on("paneladded", this.validateVisibility, this);
38710             this.on("panelremoved", this.validateVisibility, this);
38711         }
38712         if(this.autoScroll){
38713             this.bodyEl.setStyle("overflow", "auto");
38714         }else{
38715             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38716         }
38717         //if(c.titlebar !== false){
38718             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38719                 this.titleEl.hide();
38720             }else{
38721                 this.titleEl.show();
38722                 if(this.config.title){
38723                     this.titleTextEl.innerHTML = this.config.title;
38724                 }
38725             }
38726         //}
38727         if(this.config.collapsed){
38728             this.collapse(true);
38729         }
38730         if(this.config.hidden){
38731             this.hide();
38732         }
38733         
38734         if (this.unrendered_panels && this.unrendered_panels.length) {
38735             for (var i =0;i< this.unrendered_panels.length; i++) {
38736                 this.add(this.unrendered_panels[i]);
38737             }
38738             this.unrendered_panels = null;
38739             
38740         }
38741         
38742     },
38743     
38744     applyConfig : function(c)
38745     {
38746         /*
38747          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38748             var dh = Roo.DomHelper;
38749             if(c.titlebar !== false){
38750                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38751                 this.collapseBtn.on("click", this.collapse, this);
38752                 this.collapseBtn.enableDisplayMode();
38753                 /*
38754                 if(c.showPin === true || this.showPin){
38755                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38756                     this.stickBtn.enableDisplayMode();
38757                     this.stickBtn.on("click", this.expand, this);
38758                     this.stickBtn.hide();
38759                 }
38760                 
38761             }
38762             */
38763             /** This region's collapsed element
38764             * @type Roo.Element */
38765             /*
38766              *
38767             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38768                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38769             ]}, true);
38770             
38771             if(c.floatable !== false){
38772                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38773                this.collapsedEl.on("click", this.collapseClick, this);
38774             }
38775
38776             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38777                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38778                    id: "message", unselectable: "on", style:{"float":"left"}});
38779                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38780              }
38781             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38782             this.expandBtn.on("click", this.expand, this);
38783             
38784         }
38785         
38786         if(this.collapseBtn){
38787             this.collapseBtn.setVisible(c.collapsible == true);
38788         }
38789         
38790         this.cmargins = c.cmargins || this.cmargins ||
38791                          (this.position == "west" || this.position == "east" ?
38792                              {top: 0, left: 2, right:2, bottom: 0} :
38793                              {top: 2, left: 0, right:0, bottom: 2});
38794         */
38795         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38796         
38797         
38798         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38799         
38800         this.autoScroll = c.autoScroll || false;
38801         
38802         
38803        
38804         
38805         this.duration = c.duration || .30;
38806         this.slideDuration = c.slideDuration || .45;
38807         this.config = c;
38808        
38809     },
38810     /**
38811      * Returns true if this region is currently visible.
38812      * @return {Boolean}
38813      */
38814     isVisible : function(){
38815         return this.visible;
38816     },
38817
38818     /**
38819      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38820      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38821      */
38822     //setCollapsedTitle : function(title){
38823     //    title = title || "&#160;";
38824      //   if(this.collapsedTitleTextEl){
38825       //      this.collapsedTitleTextEl.innerHTML = title;
38826        // }
38827     //},
38828
38829     getBox : function(){
38830         var b;
38831       //  if(!this.collapsed){
38832             b = this.el.getBox(false, true);
38833        // }else{
38834           //  b = this.collapsedEl.getBox(false, true);
38835         //}
38836         return b;
38837     },
38838
38839     getMargins : function(){
38840         return this.margins;
38841         //return this.collapsed ? this.cmargins : this.margins;
38842     },
38843 /*
38844     highlight : function(){
38845         this.el.addClass("x-layout-panel-dragover");
38846     },
38847
38848     unhighlight : function(){
38849         this.el.removeClass("x-layout-panel-dragover");
38850     },
38851 */
38852     updateBox : function(box)
38853     {
38854         if (!this.bodyEl) {
38855             return; // not rendered yet..
38856         }
38857         
38858         this.box = box;
38859         if(!this.collapsed){
38860             this.el.dom.style.left = box.x + "px";
38861             this.el.dom.style.top = box.y + "px";
38862             this.updateBody(box.width, box.height);
38863         }else{
38864             this.collapsedEl.dom.style.left = box.x + "px";
38865             this.collapsedEl.dom.style.top = box.y + "px";
38866             this.collapsedEl.setSize(box.width, box.height);
38867         }
38868         if(this.tabs){
38869             this.tabs.autoSizeTabs();
38870         }
38871     },
38872
38873     updateBody : function(w, h)
38874     {
38875         if(w !== null){
38876             this.el.setWidth(w);
38877             w -= this.el.getBorderWidth("rl");
38878             if(this.config.adjustments){
38879                 w += this.config.adjustments[0];
38880             }
38881         }
38882         if(h !== null && h > 0){
38883             this.el.setHeight(h);
38884             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38885             h -= this.el.getBorderWidth("tb");
38886             if(this.config.adjustments){
38887                 h += this.config.adjustments[1];
38888             }
38889             this.bodyEl.setHeight(h);
38890             if(this.tabs){
38891                 h = this.tabs.syncHeight(h);
38892             }
38893         }
38894         if(this.panelSize){
38895             w = w !== null ? w : this.panelSize.width;
38896             h = h !== null ? h : this.panelSize.height;
38897         }
38898         if(this.activePanel){
38899             var el = this.activePanel.getEl();
38900             w = w !== null ? w : el.getWidth();
38901             h = h !== null ? h : el.getHeight();
38902             this.panelSize = {width: w, height: h};
38903             this.activePanel.setSize(w, h);
38904         }
38905         if(Roo.isIE && this.tabs){
38906             this.tabs.el.repaint();
38907         }
38908     },
38909
38910     /**
38911      * Returns the container element for this region.
38912      * @return {Roo.Element}
38913      */
38914     getEl : function(){
38915         return this.el;
38916     },
38917
38918     /**
38919      * Hides this region.
38920      */
38921     hide : function(){
38922         //if(!this.collapsed){
38923             this.el.dom.style.left = "-2000px";
38924             this.el.hide();
38925         //}else{
38926          //   this.collapsedEl.dom.style.left = "-2000px";
38927          //   this.collapsedEl.hide();
38928        // }
38929         this.visible = false;
38930         this.fireEvent("visibilitychange", this, false);
38931     },
38932
38933     /**
38934      * Shows this region if it was previously hidden.
38935      */
38936     show : function(){
38937         //if(!this.collapsed){
38938             this.el.show();
38939         //}else{
38940         //    this.collapsedEl.show();
38941        // }
38942         this.visible = true;
38943         this.fireEvent("visibilitychange", this, true);
38944     },
38945 /*
38946     closeClicked : function(){
38947         if(this.activePanel){
38948             this.remove(this.activePanel);
38949         }
38950     },
38951
38952     collapseClick : function(e){
38953         if(this.isSlid){
38954            e.stopPropagation();
38955            this.slideIn();
38956         }else{
38957            e.stopPropagation();
38958            this.slideOut();
38959         }
38960     },
38961 */
38962     /**
38963      * Collapses this region.
38964      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38965      */
38966     /*
38967     collapse : function(skipAnim, skipCheck = false){
38968         if(this.collapsed) {
38969             return;
38970         }
38971         
38972         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38973             
38974             this.collapsed = true;
38975             if(this.split){
38976                 this.split.el.hide();
38977             }
38978             if(this.config.animate && skipAnim !== true){
38979                 this.fireEvent("invalidated", this);
38980                 this.animateCollapse();
38981             }else{
38982                 this.el.setLocation(-20000,-20000);
38983                 this.el.hide();
38984                 this.collapsedEl.show();
38985                 this.fireEvent("collapsed", this);
38986                 this.fireEvent("invalidated", this);
38987             }
38988         }
38989         
38990     },
38991 */
38992     animateCollapse : function(){
38993         // overridden
38994     },
38995
38996     /**
38997      * Expands this region if it was previously collapsed.
38998      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38999      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39000      */
39001     /*
39002     expand : function(e, skipAnim){
39003         if(e) {
39004             e.stopPropagation();
39005         }
39006         if(!this.collapsed || this.el.hasActiveFx()) {
39007             return;
39008         }
39009         if(this.isSlid){
39010             this.afterSlideIn();
39011             skipAnim = true;
39012         }
39013         this.collapsed = false;
39014         if(this.config.animate && skipAnim !== true){
39015             this.animateExpand();
39016         }else{
39017             this.el.show();
39018             if(this.split){
39019                 this.split.el.show();
39020             }
39021             this.collapsedEl.setLocation(-2000,-2000);
39022             this.collapsedEl.hide();
39023             this.fireEvent("invalidated", this);
39024             this.fireEvent("expanded", this);
39025         }
39026     },
39027 */
39028     animateExpand : function(){
39029         // overridden
39030     },
39031
39032     initTabs : function()
39033     {
39034         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39035         
39036         var ts = new Roo.bootstrap.panel.Tabs({
39037             el: this.bodyEl.dom,
39038             region : this,
39039             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39040             disableTooltips: this.config.disableTabTips,
39041             toolbar : this.config.toolbar
39042         });
39043         
39044         if(this.config.hideTabs){
39045             ts.stripWrap.setDisplayed(false);
39046         }
39047         this.tabs = ts;
39048         ts.resizeTabs = this.config.resizeTabs === true;
39049         ts.minTabWidth = this.config.minTabWidth || 40;
39050         ts.maxTabWidth = this.config.maxTabWidth || 250;
39051         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39052         ts.monitorResize = false;
39053         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39054         ts.bodyEl.addClass('roo-layout-tabs-body');
39055         this.panels.each(this.initPanelAsTab, this);
39056     },
39057
39058     initPanelAsTab : function(panel){
39059         var ti = this.tabs.addTab(
39060             panel.getEl().id,
39061             panel.getTitle(),
39062             null,
39063             this.config.closeOnTab && panel.isClosable(),
39064             panel.tpl
39065         );
39066         if(panel.tabTip !== undefined){
39067             ti.setTooltip(panel.tabTip);
39068         }
39069         ti.on("activate", function(){
39070               this.setActivePanel(panel);
39071         }, this);
39072         
39073         if(this.config.closeOnTab){
39074             ti.on("beforeclose", function(t, e){
39075                 e.cancel = true;
39076                 this.remove(panel);
39077             }, this);
39078         }
39079         
39080         panel.tabItem = ti;
39081         
39082         return ti;
39083     },
39084
39085     updatePanelTitle : function(panel, title)
39086     {
39087         if(this.activePanel == panel){
39088             this.updateTitle(title);
39089         }
39090         if(this.tabs){
39091             var ti = this.tabs.getTab(panel.getEl().id);
39092             ti.setText(title);
39093             if(panel.tabTip !== undefined){
39094                 ti.setTooltip(panel.tabTip);
39095             }
39096         }
39097     },
39098
39099     updateTitle : function(title){
39100         if(this.titleTextEl && !this.config.title){
39101             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39102         }
39103     },
39104
39105     setActivePanel : function(panel)
39106     {
39107         panel = this.getPanel(panel);
39108         if(this.activePanel && this.activePanel != panel){
39109             if(this.activePanel.setActiveState(false) === false){
39110                 return;
39111             }
39112         }
39113         this.activePanel = panel;
39114         panel.setActiveState(true);
39115         if(this.panelSize){
39116             panel.setSize(this.panelSize.width, this.panelSize.height);
39117         }
39118         if(this.closeBtn){
39119             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39120         }
39121         this.updateTitle(panel.getTitle());
39122         if(this.tabs){
39123             this.fireEvent("invalidated", this);
39124         }
39125         this.fireEvent("panelactivated", this, panel);
39126     },
39127
39128     /**
39129      * Shows the specified panel.
39130      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39131      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39132      */
39133     showPanel : function(panel)
39134     {
39135         panel = this.getPanel(panel);
39136         if(panel){
39137             if(this.tabs){
39138                 var tab = this.tabs.getTab(panel.getEl().id);
39139                 if(tab.isHidden()){
39140                     this.tabs.unhideTab(tab.id);
39141                 }
39142                 tab.activate();
39143             }else{
39144                 this.setActivePanel(panel);
39145             }
39146         }
39147         return panel;
39148     },
39149
39150     /**
39151      * Get the active panel for this region.
39152      * @return {Roo.ContentPanel} The active panel or null
39153      */
39154     getActivePanel : function(){
39155         return this.activePanel;
39156     },
39157
39158     validateVisibility : function(){
39159         if(this.panels.getCount() < 1){
39160             this.updateTitle("&#160;");
39161             this.closeBtn.hide();
39162             this.hide();
39163         }else{
39164             if(!this.isVisible()){
39165                 this.show();
39166             }
39167         }
39168     },
39169
39170     /**
39171      * Adds the passed ContentPanel(s) to this region.
39172      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39173      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39174      */
39175     add : function(panel)
39176     {
39177         if(arguments.length > 1){
39178             for(var i = 0, len = arguments.length; i < len; i++) {
39179                 this.add(arguments[i]);
39180             }
39181             return null;
39182         }
39183         
39184         // if we have not been rendered yet, then we can not really do much of this..
39185         if (!this.bodyEl) {
39186             this.unrendered_panels.push(panel);
39187             return panel;
39188         }
39189         
39190         
39191         
39192         
39193         if(this.hasPanel(panel)){
39194             this.showPanel(panel);
39195             return panel;
39196         }
39197         panel.setRegion(this);
39198         this.panels.add(panel);
39199        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39200             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39201             // and hide them... ???
39202             this.bodyEl.dom.appendChild(panel.getEl().dom);
39203             if(panel.background !== true){
39204                 this.setActivePanel(panel);
39205             }
39206             this.fireEvent("paneladded", this, panel);
39207             return panel;
39208         }
39209         */
39210         if(!this.tabs){
39211             this.initTabs();
39212         }else{
39213             this.initPanelAsTab(panel);
39214         }
39215         
39216         
39217         if(panel.background !== true){
39218             this.tabs.activate(panel.getEl().id);
39219         }
39220         this.fireEvent("paneladded", this, panel);
39221         return panel;
39222     },
39223
39224     /**
39225      * Hides the tab for the specified panel.
39226      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39227      */
39228     hidePanel : function(panel){
39229         if(this.tabs && (panel = this.getPanel(panel))){
39230             this.tabs.hideTab(panel.getEl().id);
39231         }
39232     },
39233
39234     /**
39235      * Unhides the tab for a previously hidden panel.
39236      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39237      */
39238     unhidePanel : function(panel){
39239         if(this.tabs && (panel = this.getPanel(panel))){
39240             this.tabs.unhideTab(panel.getEl().id);
39241         }
39242     },
39243
39244     clearPanels : function(){
39245         while(this.panels.getCount() > 0){
39246              this.remove(this.panels.first());
39247         }
39248     },
39249
39250     /**
39251      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39252      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39253      * @param {Boolean} preservePanel Overrides the config preservePanel option
39254      * @return {Roo.ContentPanel} The panel that was removed
39255      */
39256     remove : function(panel, preservePanel)
39257     {
39258         panel = this.getPanel(panel);
39259         if(!panel){
39260             return null;
39261         }
39262         var e = {};
39263         this.fireEvent("beforeremove", this, panel, e);
39264         if(e.cancel === true){
39265             return null;
39266         }
39267         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39268         var panelId = panel.getId();
39269         this.panels.removeKey(panelId);
39270         if(preservePanel){
39271             document.body.appendChild(panel.getEl().dom);
39272         }
39273         if(this.tabs){
39274             this.tabs.removeTab(panel.getEl().id);
39275         }else if (!preservePanel){
39276             this.bodyEl.dom.removeChild(panel.getEl().dom);
39277         }
39278         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39279             var p = this.panels.first();
39280             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39281             tempEl.appendChild(p.getEl().dom);
39282             this.bodyEl.update("");
39283             this.bodyEl.dom.appendChild(p.getEl().dom);
39284             tempEl = null;
39285             this.updateTitle(p.getTitle());
39286             this.tabs = null;
39287             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39288             this.setActivePanel(p);
39289         }
39290         panel.setRegion(null);
39291         if(this.activePanel == panel){
39292             this.activePanel = null;
39293         }
39294         if(this.config.autoDestroy !== false && preservePanel !== true){
39295             try{panel.destroy();}catch(e){}
39296         }
39297         this.fireEvent("panelremoved", this, panel);
39298         return panel;
39299     },
39300
39301     /**
39302      * Returns the TabPanel component used by this region
39303      * @return {Roo.TabPanel}
39304      */
39305     getTabs : function(){
39306         return this.tabs;
39307     },
39308
39309     createTool : function(parentEl, className){
39310         var btn = Roo.DomHelper.append(parentEl, {
39311             tag: "div",
39312             cls: "x-layout-tools-button",
39313             children: [ {
39314                 tag: "div",
39315                 cls: "roo-layout-tools-button-inner " + className,
39316                 html: "&#160;"
39317             }]
39318         }, true);
39319         btn.addClassOnOver("roo-layout-tools-button-over");
39320         return btn;
39321     }
39322 });/*
39323  * Based on:
39324  * Ext JS Library 1.1.1
39325  * Copyright(c) 2006-2007, Ext JS, LLC.
39326  *
39327  * Originally Released Under LGPL - original licence link has changed is not relivant.
39328  *
39329  * Fork - LGPL
39330  * <script type="text/javascript">
39331  */
39332  
39333
39334
39335 /**
39336  * @class Roo.SplitLayoutRegion
39337  * @extends Roo.LayoutRegion
39338  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39339  */
39340 Roo.bootstrap.layout.Split = function(config){
39341     this.cursor = config.cursor;
39342     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39343 };
39344
39345 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39346 {
39347     splitTip : "Drag to resize.",
39348     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39349     useSplitTips : false,
39350
39351     applyConfig : function(config){
39352         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39353     },
39354     
39355     onRender : function(ctr,pos) {
39356         
39357         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39358         if(!this.config.split){
39359             return;
39360         }
39361         if(!this.split){
39362             
39363             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39364                             tag: "div",
39365                             id: this.el.id + "-split",
39366                             cls: "roo-layout-split roo-layout-split-"+this.position,
39367                             html: "&#160;"
39368             });
39369             /** The SplitBar for this region 
39370             * @type Roo.SplitBar */
39371             // does not exist yet...
39372             Roo.log([this.position, this.orientation]);
39373             
39374             this.split = new Roo.bootstrap.SplitBar({
39375                 dragElement : splitEl,
39376                 resizingElement: this.el,
39377                 orientation : this.orientation
39378             });
39379             
39380             this.split.on("moved", this.onSplitMove, this);
39381             this.split.useShim = this.config.useShim === true;
39382             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39383             if(this.useSplitTips){
39384                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39385             }
39386             //if(config.collapsible){
39387             //    this.split.el.on("dblclick", this.collapse,  this);
39388             //}
39389         }
39390         if(typeof this.config.minSize != "undefined"){
39391             this.split.minSize = this.config.minSize;
39392         }
39393         if(typeof this.config.maxSize != "undefined"){
39394             this.split.maxSize = this.config.maxSize;
39395         }
39396         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39397             this.hideSplitter();
39398         }
39399         
39400     },
39401
39402     getHMaxSize : function(){
39403          var cmax = this.config.maxSize || 10000;
39404          var center = this.mgr.getRegion("center");
39405          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39406     },
39407
39408     getVMaxSize : function(){
39409          var cmax = this.config.maxSize || 10000;
39410          var center = this.mgr.getRegion("center");
39411          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39412     },
39413
39414     onSplitMove : function(split, newSize){
39415         this.fireEvent("resized", this, newSize);
39416     },
39417     
39418     /** 
39419      * Returns the {@link Roo.SplitBar} for this region.
39420      * @return {Roo.SplitBar}
39421      */
39422     getSplitBar : function(){
39423         return this.split;
39424     },
39425     
39426     hide : function(){
39427         this.hideSplitter();
39428         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39429     },
39430
39431     hideSplitter : function(){
39432         if(this.split){
39433             this.split.el.setLocation(-2000,-2000);
39434             this.split.el.hide();
39435         }
39436     },
39437
39438     show : function(){
39439         if(this.split){
39440             this.split.el.show();
39441         }
39442         Roo.bootstrap.layout.Split.superclass.show.call(this);
39443     },
39444     
39445     beforeSlide: function(){
39446         if(Roo.isGecko){// firefox overflow auto bug workaround
39447             this.bodyEl.clip();
39448             if(this.tabs) {
39449                 this.tabs.bodyEl.clip();
39450             }
39451             if(this.activePanel){
39452                 this.activePanel.getEl().clip();
39453                 
39454                 if(this.activePanel.beforeSlide){
39455                     this.activePanel.beforeSlide();
39456                 }
39457             }
39458         }
39459     },
39460     
39461     afterSlide : function(){
39462         if(Roo.isGecko){// firefox overflow auto bug workaround
39463             this.bodyEl.unclip();
39464             if(this.tabs) {
39465                 this.tabs.bodyEl.unclip();
39466             }
39467             if(this.activePanel){
39468                 this.activePanel.getEl().unclip();
39469                 if(this.activePanel.afterSlide){
39470                     this.activePanel.afterSlide();
39471                 }
39472             }
39473         }
39474     },
39475
39476     initAutoHide : function(){
39477         if(this.autoHide !== false){
39478             if(!this.autoHideHd){
39479                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39480                 this.autoHideHd = {
39481                     "mouseout": function(e){
39482                         if(!e.within(this.el, true)){
39483                             st.delay(500);
39484                         }
39485                     },
39486                     "mouseover" : function(e){
39487                         st.cancel();
39488                     },
39489                     scope : this
39490                 };
39491             }
39492             this.el.on(this.autoHideHd);
39493         }
39494     },
39495
39496     clearAutoHide : function(){
39497         if(this.autoHide !== false){
39498             this.el.un("mouseout", this.autoHideHd.mouseout);
39499             this.el.un("mouseover", this.autoHideHd.mouseover);
39500         }
39501     },
39502
39503     clearMonitor : function(){
39504         Roo.get(document).un("click", this.slideInIf, this);
39505     },
39506
39507     // these names are backwards but not changed for compat
39508     slideOut : function(){
39509         if(this.isSlid || this.el.hasActiveFx()){
39510             return;
39511         }
39512         this.isSlid = true;
39513         if(this.collapseBtn){
39514             this.collapseBtn.hide();
39515         }
39516         this.closeBtnState = this.closeBtn.getStyle('display');
39517         this.closeBtn.hide();
39518         if(this.stickBtn){
39519             this.stickBtn.show();
39520         }
39521         this.el.show();
39522         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39523         this.beforeSlide();
39524         this.el.setStyle("z-index", 10001);
39525         this.el.slideIn(this.getSlideAnchor(), {
39526             callback: function(){
39527                 this.afterSlide();
39528                 this.initAutoHide();
39529                 Roo.get(document).on("click", this.slideInIf, this);
39530                 this.fireEvent("slideshow", this);
39531             },
39532             scope: this,
39533             block: true
39534         });
39535     },
39536
39537     afterSlideIn : function(){
39538         this.clearAutoHide();
39539         this.isSlid = false;
39540         this.clearMonitor();
39541         this.el.setStyle("z-index", "");
39542         if(this.collapseBtn){
39543             this.collapseBtn.show();
39544         }
39545         this.closeBtn.setStyle('display', this.closeBtnState);
39546         if(this.stickBtn){
39547             this.stickBtn.hide();
39548         }
39549         this.fireEvent("slidehide", this);
39550     },
39551
39552     slideIn : function(cb){
39553         if(!this.isSlid || this.el.hasActiveFx()){
39554             Roo.callback(cb);
39555             return;
39556         }
39557         this.isSlid = false;
39558         this.beforeSlide();
39559         this.el.slideOut(this.getSlideAnchor(), {
39560             callback: function(){
39561                 this.el.setLeftTop(-10000, -10000);
39562                 this.afterSlide();
39563                 this.afterSlideIn();
39564                 Roo.callback(cb);
39565             },
39566             scope: this,
39567             block: true
39568         });
39569     },
39570     
39571     slideInIf : function(e){
39572         if(!e.within(this.el)){
39573             this.slideIn();
39574         }
39575     },
39576
39577     animateCollapse : function(){
39578         this.beforeSlide();
39579         this.el.setStyle("z-index", 20000);
39580         var anchor = this.getSlideAnchor();
39581         this.el.slideOut(anchor, {
39582             callback : function(){
39583                 this.el.setStyle("z-index", "");
39584                 this.collapsedEl.slideIn(anchor, {duration:.3});
39585                 this.afterSlide();
39586                 this.el.setLocation(-10000,-10000);
39587                 this.el.hide();
39588                 this.fireEvent("collapsed", this);
39589             },
39590             scope: this,
39591             block: true
39592         });
39593     },
39594
39595     animateExpand : function(){
39596         this.beforeSlide();
39597         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39598         this.el.setStyle("z-index", 20000);
39599         this.collapsedEl.hide({
39600             duration:.1
39601         });
39602         this.el.slideIn(this.getSlideAnchor(), {
39603             callback : function(){
39604                 this.el.setStyle("z-index", "");
39605                 this.afterSlide();
39606                 if(this.split){
39607                     this.split.el.show();
39608                 }
39609                 this.fireEvent("invalidated", this);
39610                 this.fireEvent("expanded", this);
39611             },
39612             scope: this,
39613             block: true
39614         });
39615     },
39616
39617     anchors : {
39618         "west" : "left",
39619         "east" : "right",
39620         "north" : "top",
39621         "south" : "bottom"
39622     },
39623
39624     sanchors : {
39625         "west" : "l",
39626         "east" : "r",
39627         "north" : "t",
39628         "south" : "b"
39629     },
39630
39631     canchors : {
39632         "west" : "tl-tr",
39633         "east" : "tr-tl",
39634         "north" : "tl-bl",
39635         "south" : "bl-tl"
39636     },
39637
39638     getAnchor : function(){
39639         return this.anchors[this.position];
39640     },
39641
39642     getCollapseAnchor : function(){
39643         return this.canchors[this.position];
39644     },
39645
39646     getSlideAnchor : function(){
39647         return this.sanchors[this.position];
39648     },
39649
39650     getAlignAdj : function(){
39651         var cm = this.cmargins;
39652         switch(this.position){
39653             case "west":
39654                 return [0, 0];
39655             break;
39656             case "east":
39657                 return [0, 0];
39658             break;
39659             case "north":
39660                 return [0, 0];
39661             break;
39662             case "south":
39663                 return [0, 0];
39664             break;
39665         }
39666     },
39667
39668     getExpandAdj : function(){
39669         var c = this.collapsedEl, cm = this.cmargins;
39670         switch(this.position){
39671             case "west":
39672                 return [-(cm.right+c.getWidth()+cm.left), 0];
39673             break;
39674             case "east":
39675                 return [cm.right+c.getWidth()+cm.left, 0];
39676             break;
39677             case "north":
39678                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39679             break;
39680             case "south":
39681                 return [0, cm.top+cm.bottom+c.getHeight()];
39682             break;
39683         }
39684     }
39685 });/*
39686  * Based on:
39687  * Ext JS Library 1.1.1
39688  * Copyright(c) 2006-2007, Ext JS, LLC.
39689  *
39690  * Originally Released Under LGPL - original licence link has changed is not relivant.
39691  *
39692  * Fork - LGPL
39693  * <script type="text/javascript">
39694  */
39695 /*
39696  * These classes are private internal classes
39697  */
39698 Roo.bootstrap.layout.Center = function(config){
39699     config.region = "center";
39700     Roo.bootstrap.layout.Region.call(this, config);
39701     this.visible = true;
39702     this.minWidth = config.minWidth || 20;
39703     this.minHeight = config.minHeight || 20;
39704 };
39705
39706 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39707     hide : function(){
39708         // center panel can't be hidden
39709     },
39710     
39711     show : function(){
39712         // center panel can't be hidden
39713     },
39714     
39715     getMinWidth: function(){
39716         return this.minWidth;
39717     },
39718     
39719     getMinHeight: function(){
39720         return this.minHeight;
39721     }
39722 });
39723
39724
39725
39726
39727  
39728
39729
39730
39731
39732
39733
39734 Roo.bootstrap.layout.North = function(config)
39735 {
39736     config.region = 'north';
39737     config.cursor = 'n-resize';
39738     
39739     Roo.bootstrap.layout.Split.call(this, config);
39740     
39741     
39742     if(this.split){
39743         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39744         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39745         this.split.el.addClass("roo-layout-split-v");
39746     }
39747     //var size = config.initialSize || config.height;
39748     //if(this.el && typeof size != "undefined"){
39749     //    this.el.setHeight(size);
39750     //}
39751 };
39752 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39753 {
39754     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39755      
39756      
39757     onRender : function(ctr, pos)
39758     {
39759         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39760         var size = this.config.initialSize || this.config.height;
39761         if(this.el && typeof size != "undefined"){
39762             this.el.setHeight(size);
39763         }
39764     
39765     },
39766     
39767     getBox : function(){
39768         if(this.collapsed){
39769             return this.collapsedEl.getBox();
39770         }
39771         var box = this.el.getBox();
39772         if(this.split){
39773             box.height += this.split.el.getHeight();
39774         }
39775         return box;
39776     },
39777     
39778     updateBox : function(box){
39779         if(this.split && !this.collapsed){
39780             box.height -= this.split.el.getHeight();
39781             this.split.el.setLeft(box.x);
39782             this.split.el.setTop(box.y+box.height);
39783             this.split.el.setWidth(box.width);
39784         }
39785         if(this.collapsed){
39786             this.updateBody(box.width, null);
39787         }
39788         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39789     }
39790 });
39791
39792
39793
39794
39795
39796 Roo.bootstrap.layout.South = function(config){
39797     config.region = 'south';
39798     config.cursor = 's-resize';
39799     Roo.bootstrap.layout.Split.call(this, config);
39800     if(this.split){
39801         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39802         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39803         this.split.el.addClass("roo-layout-split-v");
39804     }
39805     
39806 };
39807
39808 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39809     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39810     
39811     onRender : function(ctr, pos)
39812     {
39813         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39814         var size = this.config.initialSize || this.config.height;
39815         if(this.el && typeof size != "undefined"){
39816             this.el.setHeight(size);
39817         }
39818     
39819     },
39820     
39821     getBox : function(){
39822         if(this.collapsed){
39823             return this.collapsedEl.getBox();
39824         }
39825         var box = this.el.getBox();
39826         if(this.split){
39827             var sh = this.split.el.getHeight();
39828             box.height += sh;
39829             box.y -= sh;
39830         }
39831         return box;
39832     },
39833     
39834     updateBox : function(box){
39835         if(this.split && !this.collapsed){
39836             var sh = this.split.el.getHeight();
39837             box.height -= sh;
39838             box.y += sh;
39839             this.split.el.setLeft(box.x);
39840             this.split.el.setTop(box.y-sh);
39841             this.split.el.setWidth(box.width);
39842         }
39843         if(this.collapsed){
39844             this.updateBody(box.width, null);
39845         }
39846         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39847     }
39848 });
39849
39850 Roo.bootstrap.layout.East = function(config){
39851     config.region = "east";
39852     config.cursor = "e-resize";
39853     Roo.bootstrap.layout.Split.call(this, config);
39854     if(this.split){
39855         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39856         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39857         this.split.el.addClass("roo-layout-split-h");
39858     }
39859     
39860 };
39861 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39862     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39863     
39864     onRender : function(ctr, pos)
39865     {
39866         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39867         var size = this.config.initialSize || this.config.width;
39868         if(this.el && typeof size != "undefined"){
39869             this.el.setWidth(size);
39870         }
39871     
39872     },
39873     
39874     getBox : function(){
39875         if(this.collapsed){
39876             return this.collapsedEl.getBox();
39877         }
39878         var box = this.el.getBox();
39879         if(this.split){
39880             var sw = this.split.el.getWidth();
39881             box.width += sw;
39882             box.x -= sw;
39883         }
39884         return box;
39885     },
39886
39887     updateBox : function(box){
39888         if(this.split && !this.collapsed){
39889             var sw = this.split.el.getWidth();
39890             box.width -= sw;
39891             this.split.el.setLeft(box.x);
39892             this.split.el.setTop(box.y);
39893             this.split.el.setHeight(box.height);
39894             box.x += sw;
39895         }
39896         if(this.collapsed){
39897             this.updateBody(null, box.height);
39898         }
39899         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39900     }
39901 });
39902
39903 Roo.bootstrap.layout.West = function(config){
39904     config.region = "west";
39905     config.cursor = "w-resize";
39906     
39907     Roo.bootstrap.layout.Split.call(this, config);
39908     if(this.split){
39909         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39910         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39911         this.split.el.addClass("roo-layout-split-h");
39912     }
39913     
39914 };
39915 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39916     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39917     
39918     onRender: function(ctr, pos)
39919     {
39920         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39921         var size = this.config.initialSize || this.config.width;
39922         if(typeof size != "undefined"){
39923             this.el.setWidth(size);
39924         }
39925     },
39926     
39927     getBox : function(){
39928         if(this.collapsed){
39929             return this.collapsedEl.getBox();
39930         }
39931         var box = this.el.getBox();
39932         if (box.width == 0) {
39933             box.width = this.config.width; // kludge?
39934         }
39935         if(this.split){
39936             box.width += this.split.el.getWidth();
39937         }
39938         return box;
39939     },
39940     
39941     updateBox : function(box){
39942         if(this.split && !this.collapsed){
39943             var sw = this.split.el.getWidth();
39944             box.width -= sw;
39945             this.split.el.setLeft(box.x+box.width);
39946             this.split.el.setTop(box.y);
39947             this.split.el.setHeight(box.height);
39948         }
39949         if(this.collapsed){
39950             this.updateBody(null, box.height);
39951         }
39952         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39953     }
39954 });Roo.namespace("Roo.bootstrap.panel");/*
39955  * Based on:
39956  * Ext JS Library 1.1.1
39957  * Copyright(c) 2006-2007, Ext JS, LLC.
39958  *
39959  * Originally Released Under LGPL - original licence link has changed is not relivant.
39960  *
39961  * Fork - LGPL
39962  * <script type="text/javascript">
39963  */
39964 /**
39965  * @class Roo.ContentPanel
39966  * @extends Roo.util.Observable
39967  * A basic ContentPanel element.
39968  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39969  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39970  * @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
39971  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39972  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39973  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39974  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39975  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39976  * @cfg {String} title          The title for this panel
39977  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39978  * @cfg {String} url            Calls {@link #setUrl} with this value
39979  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39980  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39981  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39982  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39983  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39984  * @cfg {Boolean} badges render the badges
39985  * @cfg {String} cls  extra classes to use  
39986  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39987
39988  * @constructor
39989  * Create a new ContentPanel.
39990  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39991  * @param {String/Object} config A string to set only the title or a config object
39992  * @param {String} content (optional) Set the HTML content for this panel
39993  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39994  */
39995 Roo.bootstrap.panel.Content = function( config){
39996     
39997     this.tpl = config.tpl || false;
39998     
39999     var el = config.el;
40000     var content = config.content;
40001
40002     if(config.autoCreate){ // xtype is available if this is called from factory
40003         el = Roo.id();
40004     }
40005     this.el = Roo.get(el);
40006     if(!this.el && config && config.autoCreate){
40007         if(typeof config.autoCreate == "object"){
40008             if(!config.autoCreate.id){
40009                 config.autoCreate.id = config.id||el;
40010             }
40011             this.el = Roo.DomHelper.append(document.body,
40012                         config.autoCreate, true);
40013         }else{
40014             var elcfg =  {
40015                 tag: "div",
40016                 cls: (config.cls || '') +
40017                     (config.background ? ' bg-' + config.background : '') +
40018                     " roo-layout-inactive-content",
40019                 id: config.id||el
40020             };
40021             if (config.iframe) {
40022                 elcfg.cn = [
40023                     {
40024                         tag : 'iframe',
40025                         style : 'border: 0px',
40026                         src : 'about:blank'
40027                     }
40028                 ];
40029             }
40030               
40031             if (config.html) {
40032                 elcfg.html = config.html;
40033                 
40034             }
40035                         
40036             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40037             if (config.iframe) {
40038                 this.iframeEl = this.el.select('iframe',true).first();
40039             }
40040             
40041         }
40042     } 
40043     this.closable = false;
40044     this.loaded = false;
40045     this.active = false;
40046    
40047       
40048     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40049         
40050         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40051         
40052         this.wrapEl = this.el; //this.el.wrap();
40053         var ti = [];
40054         if (config.toolbar.items) {
40055             ti = config.toolbar.items ;
40056             delete config.toolbar.items ;
40057         }
40058         
40059         var nitems = [];
40060         this.toolbar.render(this.wrapEl, 'before');
40061         for(var i =0;i < ti.length;i++) {
40062           //  Roo.log(['add child', items[i]]);
40063             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40064         }
40065         this.toolbar.items = nitems;
40066         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40067         delete config.toolbar;
40068         
40069     }
40070     /*
40071     // xtype created footer. - not sure if will work as we normally have to render first..
40072     if (this.footer && !this.footer.el && this.footer.xtype) {
40073         if (!this.wrapEl) {
40074             this.wrapEl = this.el.wrap();
40075         }
40076     
40077         this.footer.container = this.wrapEl.createChild();
40078          
40079         this.footer = Roo.factory(this.footer, Roo);
40080         
40081     }
40082     */
40083     
40084      if(typeof config == "string"){
40085         this.title = config;
40086     }else{
40087         Roo.apply(this, config);
40088     }
40089     
40090     if(this.resizeEl){
40091         this.resizeEl = Roo.get(this.resizeEl, true);
40092     }else{
40093         this.resizeEl = this.el;
40094     }
40095     // handle view.xtype
40096     
40097  
40098     
40099     
40100     this.addEvents({
40101         /**
40102          * @event activate
40103          * Fires when this panel is activated. 
40104          * @param {Roo.ContentPanel} this
40105          */
40106         "activate" : true,
40107         /**
40108          * @event deactivate
40109          * Fires when this panel is activated. 
40110          * @param {Roo.ContentPanel} this
40111          */
40112         "deactivate" : true,
40113
40114         /**
40115          * @event resize
40116          * Fires when this panel is resized if fitToFrame is true.
40117          * @param {Roo.ContentPanel} this
40118          * @param {Number} width The width after any component adjustments
40119          * @param {Number} height The height after any component adjustments
40120          */
40121         "resize" : true,
40122         
40123          /**
40124          * @event render
40125          * Fires when this tab is created
40126          * @param {Roo.ContentPanel} this
40127          */
40128         "render" : true
40129         
40130         
40131         
40132     });
40133     
40134
40135     
40136     
40137     if(this.autoScroll && !this.iframe){
40138         this.resizeEl.setStyle("overflow", "auto");
40139     } else {
40140         // fix randome scrolling
40141         //this.el.on('scroll', function() {
40142         //    Roo.log('fix random scolling');
40143         //    this.scrollTo('top',0); 
40144         //});
40145     }
40146     content = content || this.content;
40147     if(content){
40148         this.setContent(content);
40149     }
40150     if(config && config.url){
40151         this.setUrl(this.url, this.params, this.loadOnce);
40152     }
40153     
40154     
40155     
40156     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40157     
40158     if (this.view && typeof(this.view.xtype) != 'undefined') {
40159         this.view.el = this.el.appendChild(document.createElement("div"));
40160         this.view = Roo.factory(this.view); 
40161         this.view.render  &&  this.view.render(false, '');  
40162     }
40163     
40164     
40165     this.fireEvent('render', this);
40166 };
40167
40168 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40169     
40170     cls : '',
40171     background : '',
40172     
40173     tabTip : '',
40174     
40175     iframe : false,
40176     iframeEl : false,
40177     
40178     setRegion : function(region){
40179         this.region = region;
40180         this.setActiveClass(region && !this.background);
40181     },
40182     
40183     
40184     setActiveClass: function(state)
40185     {
40186         if(state){
40187            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40188            this.el.setStyle('position','relative');
40189         }else{
40190            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40191            this.el.setStyle('position', 'absolute');
40192         } 
40193     },
40194     
40195     /**
40196      * Returns the toolbar for this Panel if one was configured. 
40197      * @return {Roo.Toolbar} 
40198      */
40199     getToolbar : function(){
40200         return this.toolbar;
40201     },
40202     
40203     setActiveState : function(active)
40204     {
40205         this.active = active;
40206         this.setActiveClass(active);
40207         if(!active){
40208             if(this.fireEvent("deactivate", this) === false){
40209                 return false;
40210             }
40211             return true;
40212         }
40213         this.fireEvent("activate", this);
40214         return true;
40215     },
40216     /**
40217      * Updates this panel's element (not for iframe)
40218      * @param {String} content The new content
40219      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40220     */
40221     setContent : function(content, loadScripts){
40222         if (this.iframe) {
40223             return;
40224         }
40225         
40226         this.el.update(content, loadScripts);
40227     },
40228
40229     ignoreResize : function(w, h){
40230         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40231             return true;
40232         }else{
40233             this.lastSize = {width: w, height: h};
40234             return false;
40235         }
40236     },
40237     /**
40238      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40239      * @return {Roo.UpdateManager} The UpdateManager
40240      */
40241     getUpdateManager : function(){
40242         if (this.iframe) {
40243             return false;
40244         }
40245         return this.el.getUpdateManager();
40246     },
40247      /**
40248      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40249      * Does not work with IFRAME contents
40250      * @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:
40251 <pre><code>
40252 panel.load({
40253     url: "your-url.php",
40254     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40255     callback: yourFunction,
40256     scope: yourObject, //(optional scope)
40257     discardUrl: false,
40258     nocache: false,
40259     text: "Loading...",
40260     timeout: 30,
40261     scripts: false
40262 });
40263 </code></pre>
40264      
40265      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40266      * 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.
40267      * @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}
40268      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40269      * @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.
40270      * @return {Roo.ContentPanel} this
40271      */
40272     load : function(){
40273         
40274         if (this.iframe) {
40275             return this;
40276         }
40277         
40278         var um = this.el.getUpdateManager();
40279         um.update.apply(um, arguments);
40280         return this;
40281     },
40282
40283
40284     /**
40285      * 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.
40286      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40287      * @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)
40288      * @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)
40289      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40290      */
40291     setUrl : function(url, params, loadOnce){
40292         if (this.iframe) {
40293             this.iframeEl.dom.src = url;
40294             return false;
40295         }
40296         
40297         if(this.refreshDelegate){
40298             this.removeListener("activate", this.refreshDelegate);
40299         }
40300         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40301         this.on("activate", this.refreshDelegate);
40302         return this.el.getUpdateManager();
40303     },
40304     
40305     _handleRefresh : function(url, params, loadOnce){
40306         if(!loadOnce || !this.loaded){
40307             var updater = this.el.getUpdateManager();
40308             updater.update(url, params, this._setLoaded.createDelegate(this));
40309         }
40310     },
40311     
40312     _setLoaded : function(){
40313         this.loaded = true;
40314     }, 
40315     
40316     /**
40317      * Returns this panel's id
40318      * @return {String} 
40319      */
40320     getId : function(){
40321         return this.el.id;
40322     },
40323     
40324     /** 
40325      * Returns this panel's element - used by regiosn to add.
40326      * @return {Roo.Element} 
40327      */
40328     getEl : function(){
40329         return this.wrapEl || this.el;
40330     },
40331     
40332    
40333     
40334     adjustForComponents : function(width, height)
40335     {
40336         //Roo.log('adjustForComponents ');
40337         if(this.resizeEl != this.el){
40338             width -= this.el.getFrameWidth('lr');
40339             height -= this.el.getFrameWidth('tb');
40340         }
40341         if(this.toolbar){
40342             var te = this.toolbar.getEl();
40343             te.setWidth(width);
40344             height -= te.getHeight();
40345         }
40346         if(this.footer){
40347             var te = this.footer.getEl();
40348             te.setWidth(width);
40349             height -= te.getHeight();
40350         }
40351         
40352         
40353         if(this.adjustments){
40354             width += this.adjustments[0];
40355             height += this.adjustments[1];
40356         }
40357         return {"width": width, "height": height};
40358     },
40359     
40360     setSize : function(width, height){
40361         if(this.fitToFrame && !this.ignoreResize(width, height)){
40362             if(this.fitContainer && this.resizeEl != this.el){
40363                 this.el.setSize(width, height);
40364             }
40365             var size = this.adjustForComponents(width, height);
40366             if (this.iframe) {
40367                 this.iframeEl.setSize(width,height);
40368             }
40369             
40370             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40371             this.fireEvent('resize', this, size.width, size.height);
40372             
40373             
40374         }
40375     },
40376     
40377     /**
40378      * Returns this panel's title
40379      * @return {String} 
40380      */
40381     getTitle : function(){
40382         
40383         if (typeof(this.title) != 'object') {
40384             return this.title;
40385         }
40386         
40387         var t = '';
40388         for (var k in this.title) {
40389             if (!this.title.hasOwnProperty(k)) {
40390                 continue;
40391             }
40392             
40393             if (k.indexOf('-') >= 0) {
40394                 var s = k.split('-');
40395                 for (var i = 0; i<s.length; i++) {
40396                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40397                 }
40398             } else {
40399                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40400             }
40401         }
40402         return t;
40403     },
40404     
40405     /**
40406      * Set this panel's title
40407      * @param {String} title
40408      */
40409     setTitle : function(title){
40410         this.title = title;
40411         if(this.region){
40412             this.region.updatePanelTitle(this, title);
40413         }
40414     },
40415     
40416     /**
40417      * Returns true is this panel was configured to be closable
40418      * @return {Boolean} 
40419      */
40420     isClosable : function(){
40421         return this.closable;
40422     },
40423     
40424     beforeSlide : function(){
40425         this.el.clip();
40426         this.resizeEl.clip();
40427     },
40428     
40429     afterSlide : function(){
40430         this.el.unclip();
40431         this.resizeEl.unclip();
40432     },
40433     
40434     /**
40435      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40436      *   Will fail silently if the {@link #setUrl} method has not been called.
40437      *   This does not activate the panel, just updates its content.
40438      */
40439     refresh : function(){
40440         if(this.refreshDelegate){
40441            this.loaded = false;
40442            this.refreshDelegate();
40443         }
40444     },
40445     
40446     /**
40447      * Destroys this panel
40448      */
40449     destroy : function(){
40450         this.el.removeAllListeners();
40451         var tempEl = document.createElement("span");
40452         tempEl.appendChild(this.el.dom);
40453         tempEl.innerHTML = "";
40454         this.el.remove();
40455         this.el = null;
40456     },
40457     
40458     /**
40459      * form - if the content panel contains a form - this is a reference to it.
40460      * @type {Roo.form.Form}
40461      */
40462     form : false,
40463     /**
40464      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40465      *    This contains a reference to it.
40466      * @type {Roo.View}
40467      */
40468     view : false,
40469     
40470       /**
40471      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40472      * <pre><code>
40473
40474 layout.addxtype({
40475        xtype : 'Form',
40476        items: [ .... ]
40477    }
40478 );
40479
40480 </code></pre>
40481      * @param {Object} cfg Xtype definition of item to add.
40482      */
40483     
40484     
40485     getChildContainer: function () {
40486         return this.getEl();
40487     }
40488     
40489     
40490     /*
40491         var  ret = new Roo.factory(cfg);
40492         return ret;
40493         
40494         
40495         // add form..
40496         if (cfg.xtype.match(/^Form$/)) {
40497             
40498             var el;
40499             //if (this.footer) {
40500             //    el = this.footer.container.insertSibling(false, 'before');
40501             //} else {
40502                 el = this.el.createChild();
40503             //}
40504
40505             this.form = new  Roo.form.Form(cfg);
40506             
40507             
40508             if ( this.form.allItems.length) {
40509                 this.form.render(el.dom);
40510             }
40511             return this.form;
40512         }
40513         // should only have one of theses..
40514         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40515             // views.. should not be just added - used named prop 'view''
40516             
40517             cfg.el = this.el.appendChild(document.createElement("div"));
40518             // factory?
40519             
40520             var ret = new Roo.factory(cfg);
40521              
40522              ret.render && ret.render(false, ''); // render blank..
40523             this.view = ret;
40524             return ret;
40525         }
40526         return false;
40527     }
40528     \*/
40529 });
40530  
40531 /**
40532  * @class Roo.bootstrap.panel.Grid
40533  * @extends Roo.bootstrap.panel.Content
40534  * @constructor
40535  * Create a new GridPanel.
40536  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40537  * @param {Object} config A the config object
40538   
40539  */
40540
40541
40542
40543 Roo.bootstrap.panel.Grid = function(config)
40544 {
40545     
40546       
40547     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40548         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40549
40550     config.el = this.wrapper;
40551     //this.el = this.wrapper;
40552     
40553       if (config.container) {
40554         // ctor'ed from a Border/panel.grid
40555         
40556         
40557         this.wrapper.setStyle("overflow", "hidden");
40558         this.wrapper.addClass('roo-grid-container');
40559
40560     }
40561     
40562     
40563     if(config.toolbar){
40564         var tool_el = this.wrapper.createChild();    
40565         this.toolbar = Roo.factory(config.toolbar);
40566         var ti = [];
40567         if (config.toolbar.items) {
40568             ti = config.toolbar.items ;
40569             delete config.toolbar.items ;
40570         }
40571         
40572         var nitems = [];
40573         this.toolbar.render(tool_el);
40574         for(var i =0;i < ti.length;i++) {
40575           //  Roo.log(['add child', items[i]]);
40576             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40577         }
40578         this.toolbar.items = nitems;
40579         
40580         delete config.toolbar;
40581     }
40582     
40583     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40584     config.grid.scrollBody = true;;
40585     config.grid.monitorWindowResize = false; // turn off autosizing
40586     config.grid.autoHeight = false;
40587     config.grid.autoWidth = false;
40588     
40589     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40590     
40591     if (config.background) {
40592         // render grid on panel activation (if panel background)
40593         this.on('activate', function(gp) {
40594             if (!gp.grid.rendered) {
40595                 gp.grid.render(this.wrapper);
40596                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40597             }
40598         });
40599             
40600     } else {
40601         this.grid.render(this.wrapper);
40602         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40603
40604     }
40605     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40606     // ??? needed ??? config.el = this.wrapper;
40607     
40608     
40609     
40610   
40611     // xtype created footer. - not sure if will work as we normally have to render first..
40612     if (this.footer && !this.footer.el && this.footer.xtype) {
40613         
40614         var ctr = this.grid.getView().getFooterPanel(true);
40615         this.footer.dataSource = this.grid.dataSource;
40616         this.footer = Roo.factory(this.footer, Roo);
40617         this.footer.render(ctr);
40618         
40619     }
40620     
40621     
40622     
40623     
40624      
40625 };
40626
40627 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40628     getId : function(){
40629         return this.grid.id;
40630     },
40631     
40632     /**
40633      * Returns the grid for this panel
40634      * @return {Roo.bootstrap.Table} 
40635      */
40636     getGrid : function(){
40637         return this.grid;    
40638     },
40639     
40640     setSize : function(width, height){
40641         if(!this.ignoreResize(width, height)){
40642             var grid = this.grid;
40643             var size = this.adjustForComponents(width, height);
40644             // tfoot is not a footer?
40645           
40646             
40647             var gridel = grid.getGridEl();
40648             gridel.setSize(size.width, size.height);
40649             
40650             var tbd = grid.getGridEl().select('tbody', true).first();
40651             var thd = grid.getGridEl().select('thead',true).first();
40652             var tbf= grid.getGridEl().select('tfoot', true).first();
40653
40654             if (tbf) {
40655                 size.height -= tbf.getHeight();
40656             }
40657             if (thd) {
40658                 size.height -= thd.getHeight();
40659             }
40660             
40661             tbd.setSize(size.width, size.height );
40662             // this is for the account management tab -seems to work there.
40663             var thd = grid.getGridEl().select('thead',true).first();
40664             //if (tbd) {
40665             //    tbd.setSize(size.width, size.height - thd.getHeight());
40666             //}
40667              
40668             grid.autoSize();
40669         }
40670     },
40671      
40672     
40673     
40674     beforeSlide : function(){
40675         this.grid.getView().scroller.clip();
40676     },
40677     
40678     afterSlide : function(){
40679         this.grid.getView().scroller.unclip();
40680     },
40681     
40682     destroy : function(){
40683         this.grid.destroy();
40684         delete this.grid;
40685         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40686     }
40687 });
40688
40689 /**
40690  * @class Roo.bootstrap.panel.Nest
40691  * @extends Roo.bootstrap.panel.Content
40692  * @constructor
40693  * Create a new Panel, that can contain a layout.Border.
40694  * 
40695  * 
40696  * @param {Roo.BorderLayout} layout The layout for this panel
40697  * @param {String/Object} config A string to set only the title or a config object
40698  */
40699 Roo.bootstrap.panel.Nest = function(config)
40700 {
40701     // construct with only one argument..
40702     /* FIXME - implement nicer consturctors
40703     if (layout.layout) {
40704         config = layout;
40705         layout = config.layout;
40706         delete config.layout;
40707     }
40708     if (layout.xtype && !layout.getEl) {
40709         // then layout needs constructing..
40710         layout = Roo.factory(layout, Roo);
40711     }
40712     */
40713     
40714     config.el =  config.layout.getEl();
40715     
40716     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40717     
40718     config.layout.monitorWindowResize = false; // turn off autosizing
40719     this.layout = config.layout;
40720     this.layout.getEl().addClass("roo-layout-nested-layout");
40721     this.layout.parent = this;
40722     
40723     
40724     
40725     
40726 };
40727
40728 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40729
40730     setSize : function(width, height){
40731         if(!this.ignoreResize(width, height)){
40732             var size = this.adjustForComponents(width, height);
40733             var el = this.layout.getEl();
40734             if (size.height < 1) {
40735                 el.setWidth(size.width);   
40736             } else {
40737                 el.setSize(size.width, size.height);
40738             }
40739             var touch = el.dom.offsetWidth;
40740             this.layout.layout();
40741             // ie requires a double layout on the first pass
40742             if(Roo.isIE && !this.initialized){
40743                 this.initialized = true;
40744                 this.layout.layout();
40745             }
40746         }
40747     },
40748     
40749     // activate all subpanels if not currently active..
40750     
40751     setActiveState : function(active){
40752         this.active = active;
40753         this.setActiveClass(active);
40754         
40755         if(!active){
40756             this.fireEvent("deactivate", this);
40757             return;
40758         }
40759         
40760         this.fireEvent("activate", this);
40761         // not sure if this should happen before or after..
40762         if (!this.layout) {
40763             return; // should not happen..
40764         }
40765         var reg = false;
40766         for (var r in this.layout.regions) {
40767             reg = this.layout.getRegion(r);
40768             if (reg.getActivePanel()) {
40769                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40770                 reg.setActivePanel(reg.getActivePanel());
40771                 continue;
40772             }
40773             if (!reg.panels.length) {
40774                 continue;
40775             }
40776             reg.showPanel(reg.getPanel(0));
40777         }
40778         
40779         
40780         
40781         
40782     },
40783     
40784     /**
40785      * Returns the nested BorderLayout for this panel
40786      * @return {Roo.BorderLayout} 
40787      */
40788     getLayout : function(){
40789         return this.layout;
40790     },
40791     
40792      /**
40793      * Adds a xtype elements to the layout of the nested panel
40794      * <pre><code>
40795
40796 panel.addxtype({
40797        xtype : 'ContentPanel',
40798        region: 'west',
40799        items: [ .... ]
40800    }
40801 );
40802
40803 panel.addxtype({
40804         xtype : 'NestedLayoutPanel',
40805         region: 'west',
40806         layout: {
40807            center: { },
40808            west: { }   
40809         },
40810         items : [ ... list of content panels or nested layout panels.. ]
40811    }
40812 );
40813 </code></pre>
40814      * @param {Object} cfg Xtype definition of item to add.
40815      */
40816     addxtype : function(cfg) {
40817         return this.layout.addxtype(cfg);
40818     
40819     }
40820 });/*
40821  * Based on:
40822  * Ext JS Library 1.1.1
40823  * Copyright(c) 2006-2007, Ext JS, LLC.
40824  *
40825  * Originally Released Under LGPL - original licence link has changed is not relivant.
40826  *
40827  * Fork - LGPL
40828  * <script type="text/javascript">
40829  */
40830 /**
40831  * @class Roo.TabPanel
40832  * @extends Roo.util.Observable
40833  * A lightweight tab container.
40834  * <br><br>
40835  * Usage:
40836  * <pre><code>
40837 // basic tabs 1, built from existing content
40838 var tabs = new Roo.TabPanel("tabs1");
40839 tabs.addTab("script", "View Script");
40840 tabs.addTab("markup", "View Markup");
40841 tabs.activate("script");
40842
40843 // more advanced tabs, built from javascript
40844 var jtabs = new Roo.TabPanel("jtabs");
40845 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40846
40847 // set up the UpdateManager
40848 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40849 var updater = tab2.getUpdateManager();
40850 updater.setDefaultUrl("ajax1.htm");
40851 tab2.on('activate', updater.refresh, updater, true);
40852
40853 // Use setUrl for Ajax loading
40854 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40855 tab3.setUrl("ajax2.htm", null, true);
40856
40857 // Disabled tab
40858 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40859 tab4.disable();
40860
40861 jtabs.activate("jtabs-1");
40862  * </code></pre>
40863  * @constructor
40864  * Create a new TabPanel.
40865  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40866  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40867  */
40868 Roo.bootstrap.panel.Tabs = function(config){
40869     /**
40870     * The container element for this TabPanel.
40871     * @type Roo.Element
40872     */
40873     this.el = Roo.get(config.el);
40874     delete config.el;
40875     if(config){
40876         if(typeof config == "boolean"){
40877             this.tabPosition = config ? "bottom" : "top";
40878         }else{
40879             Roo.apply(this, config);
40880         }
40881     }
40882     
40883     if(this.tabPosition == "bottom"){
40884         // if tabs are at the bottom = create the body first.
40885         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40886         this.el.addClass("roo-tabs-bottom");
40887     }
40888     // next create the tabs holders
40889     
40890     if (this.tabPosition == "west"){
40891         
40892         var reg = this.region; // fake it..
40893         while (reg) {
40894             if (!reg.mgr.parent) {
40895                 break;
40896             }
40897             reg = reg.mgr.parent.region;
40898         }
40899         Roo.log("got nest?");
40900         Roo.log(reg);
40901         if (reg.mgr.getRegion('west')) {
40902             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40903             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40904             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40905             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40906             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40907         
40908             
40909         }
40910         
40911         
40912     } else {
40913      
40914         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40915         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40916         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40917         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40918     }
40919     
40920     
40921     if(Roo.isIE){
40922         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40923     }
40924     
40925     // finally - if tabs are at the top, then create the body last..
40926     if(this.tabPosition != "bottom"){
40927         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40928          * @type Roo.Element
40929          */
40930         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40931         this.el.addClass("roo-tabs-top");
40932     }
40933     this.items = [];
40934
40935     this.bodyEl.setStyle("position", "relative");
40936
40937     this.active = null;
40938     this.activateDelegate = this.activate.createDelegate(this);
40939
40940     this.addEvents({
40941         /**
40942          * @event tabchange
40943          * Fires when the active tab changes
40944          * @param {Roo.TabPanel} this
40945          * @param {Roo.TabPanelItem} activePanel The new active tab
40946          */
40947         "tabchange": true,
40948         /**
40949          * @event beforetabchange
40950          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40951          * @param {Roo.TabPanel} this
40952          * @param {Object} e Set cancel to true on this object to cancel the tab change
40953          * @param {Roo.TabPanelItem} tab The tab being changed to
40954          */
40955         "beforetabchange" : true
40956     });
40957
40958     Roo.EventManager.onWindowResize(this.onResize, this);
40959     this.cpad = this.el.getPadding("lr");
40960     this.hiddenCount = 0;
40961
40962
40963     // toolbar on the tabbar support...
40964     if (this.toolbar) {
40965         alert("no toolbar support yet");
40966         this.toolbar  = false;
40967         /*
40968         var tcfg = this.toolbar;
40969         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40970         this.toolbar = new Roo.Toolbar(tcfg);
40971         if (Roo.isSafari) {
40972             var tbl = tcfg.container.child('table', true);
40973             tbl.setAttribute('width', '100%');
40974         }
40975         */
40976         
40977     }
40978    
40979
40980
40981     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40982 };
40983
40984 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40985     /*
40986      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40987      */
40988     tabPosition : "top",
40989     /*
40990      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40991      */
40992     currentTabWidth : 0,
40993     /*
40994      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40995      */
40996     minTabWidth : 40,
40997     /*
40998      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40999      */
41000     maxTabWidth : 250,
41001     /*
41002      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41003      */
41004     preferredTabWidth : 175,
41005     /*
41006      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41007      */
41008     resizeTabs : false,
41009     /*
41010      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41011      */
41012     monitorResize : true,
41013     /*
41014      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41015      */
41016     toolbar : false,  // set by caller..
41017     
41018     region : false, /// set by caller
41019     
41020     disableTooltips : true, // not used yet...
41021
41022     /**
41023      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41024      * @param {String} id The id of the div to use <b>or create</b>
41025      * @param {String} text The text for the tab
41026      * @param {String} content (optional) Content to put in the TabPanelItem body
41027      * @param {Boolean} closable (optional) True to create a close icon on the tab
41028      * @return {Roo.TabPanelItem} The created TabPanelItem
41029      */
41030     addTab : function(id, text, content, closable, tpl)
41031     {
41032         var item = new Roo.bootstrap.panel.TabItem({
41033             panel: this,
41034             id : id,
41035             text : text,
41036             closable : closable,
41037             tpl : tpl
41038         });
41039         this.addTabItem(item);
41040         if(content){
41041             item.setContent(content);
41042         }
41043         return item;
41044     },
41045
41046     /**
41047      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41048      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41049      * @return {Roo.TabPanelItem}
41050      */
41051     getTab : function(id){
41052         return this.items[id];
41053     },
41054
41055     /**
41056      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41057      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41058      */
41059     hideTab : function(id){
41060         var t = this.items[id];
41061         if(!t.isHidden()){
41062            t.setHidden(true);
41063            this.hiddenCount++;
41064            this.autoSizeTabs();
41065         }
41066     },
41067
41068     /**
41069      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41070      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41071      */
41072     unhideTab : function(id){
41073         var t = this.items[id];
41074         if(t.isHidden()){
41075            t.setHidden(false);
41076            this.hiddenCount--;
41077            this.autoSizeTabs();
41078         }
41079     },
41080
41081     /**
41082      * Adds an existing {@link Roo.TabPanelItem}.
41083      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41084      */
41085     addTabItem : function(item)
41086     {
41087         this.items[item.id] = item;
41088         this.items.push(item);
41089         this.autoSizeTabs();
41090       //  if(this.resizeTabs){
41091     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41092   //         this.autoSizeTabs();
41093 //        }else{
41094 //            item.autoSize();
41095        // }
41096     },
41097
41098     /**
41099      * Removes a {@link Roo.TabPanelItem}.
41100      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41101      */
41102     removeTab : function(id){
41103         var items = this.items;
41104         var tab = items[id];
41105         if(!tab) { return; }
41106         var index = items.indexOf(tab);
41107         if(this.active == tab && items.length > 1){
41108             var newTab = this.getNextAvailable(index);
41109             if(newTab) {
41110                 newTab.activate();
41111             }
41112         }
41113         this.stripEl.dom.removeChild(tab.pnode.dom);
41114         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41115             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41116         }
41117         items.splice(index, 1);
41118         delete this.items[tab.id];
41119         tab.fireEvent("close", tab);
41120         tab.purgeListeners();
41121         this.autoSizeTabs();
41122     },
41123
41124     getNextAvailable : function(start){
41125         var items = this.items;
41126         var index = start;
41127         // look for a next tab that will slide over to
41128         // replace the one being removed
41129         while(index < items.length){
41130             var item = items[++index];
41131             if(item && !item.isHidden()){
41132                 return item;
41133             }
41134         }
41135         // if one isn't found select the previous tab (on the left)
41136         index = start;
41137         while(index >= 0){
41138             var item = items[--index];
41139             if(item && !item.isHidden()){
41140                 return item;
41141             }
41142         }
41143         return null;
41144     },
41145
41146     /**
41147      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41148      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41149      */
41150     disableTab : function(id){
41151         var tab = this.items[id];
41152         if(tab && this.active != tab){
41153             tab.disable();
41154         }
41155     },
41156
41157     /**
41158      * Enables a {@link Roo.TabPanelItem} that is disabled.
41159      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41160      */
41161     enableTab : function(id){
41162         var tab = this.items[id];
41163         tab.enable();
41164     },
41165
41166     /**
41167      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41168      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41169      * @return {Roo.TabPanelItem} The TabPanelItem.
41170      */
41171     activate : function(id)
41172     {
41173         //Roo.log('activite:'  + id);
41174         
41175         var tab = this.items[id];
41176         if(!tab){
41177             return null;
41178         }
41179         if(tab == this.active || tab.disabled){
41180             return tab;
41181         }
41182         var e = {};
41183         this.fireEvent("beforetabchange", this, e, tab);
41184         if(e.cancel !== true && !tab.disabled){
41185             if(this.active){
41186                 this.active.hide();
41187             }
41188             this.active = this.items[id];
41189             this.active.show();
41190             this.fireEvent("tabchange", this, this.active);
41191         }
41192         return tab;
41193     },
41194
41195     /**
41196      * Gets the active {@link Roo.TabPanelItem}.
41197      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41198      */
41199     getActiveTab : function(){
41200         return this.active;
41201     },
41202
41203     /**
41204      * Updates the tab body element to fit the height of the container element
41205      * for overflow scrolling
41206      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41207      */
41208     syncHeight : function(targetHeight){
41209         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41210         var bm = this.bodyEl.getMargins();
41211         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41212         this.bodyEl.setHeight(newHeight);
41213         return newHeight;
41214     },
41215
41216     onResize : function(){
41217         if(this.monitorResize){
41218             this.autoSizeTabs();
41219         }
41220     },
41221
41222     /**
41223      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41224      */
41225     beginUpdate : function(){
41226         this.updating = true;
41227     },
41228
41229     /**
41230      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41231      */
41232     endUpdate : function(){
41233         this.updating = false;
41234         this.autoSizeTabs();
41235     },
41236
41237     /**
41238      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41239      */
41240     autoSizeTabs : function()
41241     {
41242         var count = this.items.length;
41243         var vcount = count - this.hiddenCount;
41244         
41245         if (vcount < 2) {
41246             this.stripEl.hide();
41247         } else {
41248             this.stripEl.show();
41249         }
41250         
41251         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41252             return;
41253         }
41254         
41255         
41256         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41257         var availWidth = Math.floor(w / vcount);
41258         var b = this.stripBody;
41259         if(b.getWidth() > w){
41260             var tabs = this.items;
41261             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41262             if(availWidth < this.minTabWidth){
41263                 /*if(!this.sleft){    // incomplete scrolling code
41264                     this.createScrollButtons();
41265                 }
41266                 this.showScroll();
41267                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41268             }
41269         }else{
41270             if(this.currentTabWidth < this.preferredTabWidth){
41271                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41272             }
41273         }
41274     },
41275
41276     /**
41277      * Returns the number of tabs in this TabPanel.
41278      * @return {Number}
41279      */
41280      getCount : function(){
41281          return this.items.length;
41282      },
41283
41284     /**
41285      * Resizes all the tabs to the passed width
41286      * @param {Number} The new width
41287      */
41288     setTabWidth : function(width){
41289         this.currentTabWidth = width;
41290         for(var i = 0, len = this.items.length; i < len; i++) {
41291                 if(!this.items[i].isHidden()) {
41292                 this.items[i].setWidth(width);
41293             }
41294         }
41295     },
41296
41297     /**
41298      * Destroys this TabPanel
41299      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41300      */
41301     destroy : function(removeEl){
41302         Roo.EventManager.removeResizeListener(this.onResize, this);
41303         for(var i = 0, len = this.items.length; i < len; i++){
41304             this.items[i].purgeListeners();
41305         }
41306         if(removeEl === true){
41307             this.el.update("");
41308             this.el.remove();
41309         }
41310     },
41311     
41312     createStrip : function(container)
41313     {
41314         var strip = document.createElement("nav");
41315         strip.className = Roo.bootstrap.version == 4 ?
41316             "navbar-light bg-light" : 
41317             "navbar navbar-default"; //"x-tabs-wrap";
41318         container.appendChild(strip);
41319         return strip;
41320     },
41321     
41322     createStripList : function(strip)
41323     {
41324         // div wrapper for retard IE
41325         // returns the "tr" element.
41326         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41327         //'<div class="x-tabs-strip-wrap">'+
41328           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41329           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41330         return strip.firstChild; //.firstChild.firstChild.firstChild;
41331     },
41332     createBody : function(container)
41333     {
41334         var body = document.createElement("div");
41335         Roo.id(body, "tab-body");
41336         //Roo.fly(body).addClass("x-tabs-body");
41337         Roo.fly(body).addClass("tab-content");
41338         container.appendChild(body);
41339         return body;
41340     },
41341     createItemBody :function(bodyEl, id){
41342         var body = Roo.getDom(id);
41343         if(!body){
41344             body = document.createElement("div");
41345             body.id = id;
41346         }
41347         //Roo.fly(body).addClass("x-tabs-item-body");
41348         Roo.fly(body).addClass("tab-pane");
41349          bodyEl.insertBefore(body, bodyEl.firstChild);
41350         return body;
41351     },
41352     /** @private */
41353     createStripElements :  function(stripEl, text, closable, tpl)
41354     {
41355         var td = document.createElement("li"); // was td..
41356         td.className = 'nav-item';
41357         
41358         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41359         
41360         
41361         stripEl.appendChild(td);
41362         /*if(closable){
41363             td.className = "x-tabs-closable";
41364             if(!this.closeTpl){
41365                 this.closeTpl = new Roo.Template(
41366                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41367                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41368                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41369                 );
41370             }
41371             var el = this.closeTpl.overwrite(td, {"text": text});
41372             var close = el.getElementsByTagName("div")[0];
41373             var inner = el.getElementsByTagName("em")[0];
41374             return {"el": el, "close": close, "inner": inner};
41375         } else {
41376         */
41377         // not sure what this is..
41378 //            if(!this.tabTpl){
41379                 //this.tabTpl = new Roo.Template(
41380                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41381                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41382                 //);
41383 //                this.tabTpl = new Roo.Template(
41384 //                   '<a href="#">' +
41385 //                   '<span unselectable="on"' +
41386 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41387 //                            ' >{text}</span></a>'
41388 //                );
41389 //                
41390 //            }
41391
41392
41393             var template = tpl || this.tabTpl || false;
41394             
41395             if(!template){
41396                 template =  new Roo.Template(
41397                         Roo.bootstrap.version == 4 ? 
41398                             (
41399                                 '<a class="nav-link" href="#" unselectable="on"' +
41400                                      (this.disableTooltips ? '' : ' title="{text}"') +
41401                                      ' >{text}</a>'
41402                             ) : (
41403                                 '<a class="nav-link" href="#">' +
41404                                 '<span unselectable="on"' +
41405                                          (this.disableTooltips ? '' : ' title="{text}"') +
41406                                     ' >{text}</span></a>'
41407                             )
41408                 );
41409             }
41410             
41411             switch (typeof(template)) {
41412                 case 'object' :
41413                     break;
41414                 case 'string' :
41415                     template = new Roo.Template(template);
41416                     break;
41417                 default :
41418                     break;
41419             }
41420             
41421             var el = template.overwrite(td, {"text": text});
41422             
41423             var inner = el.getElementsByTagName("span")[0];
41424             
41425             return {"el": el, "inner": inner};
41426             
41427     }
41428         
41429     
41430 });
41431
41432 /**
41433  * @class Roo.TabPanelItem
41434  * @extends Roo.util.Observable
41435  * Represents an individual item (tab plus body) in a TabPanel.
41436  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41437  * @param {String} id The id of this TabPanelItem
41438  * @param {String} text The text for the tab of this TabPanelItem
41439  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41440  */
41441 Roo.bootstrap.panel.TabItem = function(config){
41442     /**
41443      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41444      * @type Roo.TabPanel
41445      */
41446     this.tabPanel = config.panel;
41447     /**
41448      * The id for this TabPanelItem
41449      * @type String
41450      */
41451     this.id = config.id;
41452     /** @private */
41453     this.disabled = false;
41454     /** @private */
41455     this.text = config.text;
41456     /** @private */
41457     this.loaded = false;
41458     this.closable = config.closable;
41459
41460     /**
41461      * The body element for this TabPanelItem.
41462      * @type Roo.Element
41463      */
41464     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41465     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41466     this.bodyEl.setStyle("display", "block");
41467     this.bodyEl.setStyle("zoom", "1");
41468     //this.hideAction();
41469
41470     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41471     /** @private */
41472     this.el = Roo.get(els.el);
41473     this.inner = Roo.get(els.inner, true);
41474      this.textEl = Roo.bootstrap.version == 4 ?
41475         this.el : Roo.get(this.el.dom.firstChild, true);
41476
41477     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41478     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41479
41480     
41481 //    this.el.on("mousedown", this.onTabMouseDown, this);
41482     this.el.on("click", this.onTabClick, this);
41483     /** @private */
41484     if(config.closable){
41485         var c = Roo.get(els.close, true);
41486         c.dom.title = this.closeText;
41487         c.addClassOnOver("close-over");
41488         c.on("click", this.closeClick, this);
41489      }
41490
41491     this.addEvents({
41492          /**
41493          * @event activate
41494          * Fires when this tab becomes the active tab.
41495          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41496          * @param {Roo.TabPanelItem} this
41497          */
41498         "activate": true,
41499         /**
41500          * @event beforeclose
41501          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41502          * @param {Roo.TabPanelItem} this
41503          * @param {Object} e Set cancel to true on this object to cancel the close.
41504          */
41505         "beforeclose": true,
41506         /**
41507          * @event close
41508          * Fires when this tab is closed.
41509          * @param {Roo.TabPanelItem} this
41510          */
41511          "close": true,
41512         /**
41513          * @event deactivate
41514          * Fires when this tab is no longer the active tab.
41515          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41516          * @param {Roo.TabPanelItem} this
41517          */
41518          "deactivate" : true
41519     });
41520     this.hidden = false;
41521
41522     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41523 };
41524
41525 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41526            {
41527     purgeListeners : function(){
41528        Roo.util.Observable.prototype.purgeListeners.call(this);
41529        this.el.removeAllListeners();
41530     },
41531     /**
41532      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41533      */
41534     show : function(){
41535         this.status_node.addClass("active");
41536         this.showAction();
41537         if(Roo.isOpera){
41538             this.tabPanel.stripWrap.repaint();
41539         }
41540         this.fireEvent("activate", this.tabPanel, this);
41541     },
41542
41543     /**
41544      * Returns true if this tab is the active tab.
41545      * @return {Boolean}
41546      */
41547     isActive : function(){
41548         return this.tabPanel.getActiveTab() == this;
41549     },
41550
41551     /**
41552      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41553      */
41554     hide : function(){
41555         this.status_node.removeClass("active");
41556         this.hideAction();
41557         this.fireEvent("deactivate", this.tabPanel, this);
41558     },
41559
41560     hideAction : function(){
41561         this.bodyEl.hide();
41562         this.bodyEl.setStyle("position", "absolute");
41563         this.bodyEl.setLeft("-20000px");
41564         this.bodyEl.setTop("-20000px");
41565     },
41566
41567     showAction : function(){
41568         this.bodyEl.setStyle("position", "relative");
41569         this.bodyEl.setTop("");
41570         this.bodyEl.setLeft("");
41571         this.bodyEl.show();
41572     },
41573
41574     /**
41575      * Set the tooltip for the tab.
41576      * @param {String} tooltip The tab's tooltip
41577      */
41578     setTooltip : function(text){
41579         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41580             this.textEl.dom.qtip = text;
41581             this.textEl.dom.removeAttribute('title');
41582         }else{
41583             this.textEl.dom.title = text;
41584         }
41585     },
41586
41587     onTabClick : function(e){
41588         e.preventDefault();
41589         this.tabPanel.activate(this.id);
41590     },
41591
41592     onTabMouseDown : function(e){
41593         e.preventDefault();
41594         this.tabPanel.activate(this.id);
41595     },
41596 /*
41597     getWidth : function(){
41598         return this.inner.getWidth();
41599     },
41600
41601     setWidth : function(width){
41602         var iwidth = width - this.linode.getPadding("lr");
41603         this.inner.setWidth(iwidth);
41604         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41605         this.linode.setWidth(width);
41606     },
41607 */
41608     /**
41609      * Show or hide the tab
41610      * @param {Boolean} hidden True to hide or false to show.
41611      */
41612     setHidden : function(hidden){
41613         this.hidden = hidden;
41614         this.linode.setStyle("display", hidden ? "none" : "");
41615     },
41616
41617     /**
41618      * Returns true if this tab is "hidden"
41619      * @return {Boolean}
41620      */
41621     isHidden : function(){
41622         return this.hidden;
41623     },
41624
41625     /**
41626      * Returns the text for this tab
41627      * @return {String}
41628      */
41629     getText : function(){
41630         return this.text;
41631     },
41632     /*
41633     autoSize : function(){
41634         //this.el.beginMeasure();
41635         this.textEl.setWidth(1);
41636         /*
41637          *  #2804 [new] Tabs in Roojs
41638          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41639          */
41640         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41641         //this.el.endMeasure();
41642     //},
41643
41644     /**
41645      * Sets the text for the tab (Note: this also sets the tooltip text)
41646      * @param {String} text The tab's text and tooltip
41647      */
41648     setText : function(text){
41649         this.text = text;
41650         this.textEl.update(text);
41651         this.setTooltip(text);
41652         //if(!this.tabPanel.resizeTabs){
41653         //    this.autoSize();
41654         //}
41655     },
41656     /**
41657      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41658      */
41659     activate : function(){
41660         this.tabPanel.activate(this.id);
41661     },
41662
41663     /**
41664      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41665      */
41666     disable : function(){
41667         if(this.tabPanel.active != this){
41668             this.disabled = true;
41669             this.status_node.addClass("disabled");
41670         }
41671     },
41672
41673     /**
41674      * Enables this TabPanelItem if it was previously disabled.
41675      */
41676     enable : function(){
41677         this.disabled = false;
41678         this.status_node.removeClass("disabled");
41679     },
41680
41681     /**
41682      * Sets the content for this TabPanelItem.
41683      * @param {String} content The content
41684      * @param {Boolean} loadScripts true to look for and load scripts
41685      */
41686     setContent : function(content, loadScripts){
41687         this.bodyEl.update(content, loadScripts);
41688     },
41689
41690     /**
41691      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41692      * @return {Roo.UpdateManager} The UpdateManager
41693      */
41694     getUpdateManager : function(){
41695         return this.bodyEl.getUpdateManager();
41696     },
41697
41698     /**
41699      * Set a URL to be used to load the content for this TabPanelItem.
41700      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41701      * @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)
41702      * @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)
41703      * @return {Roo.UpdateManager} The UpdateManager
41704      */
41705     setUrl : function(url, params, loadOnce){
41706         if(this.refreshDelegate){
41707             this.un('activate', this.refreshDelegate);
41708         }
41709         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41710         this.on("activate", this.refreshDelegate);
41711         return this.bodyEl.getUpdateManager();
41712     },
41713
41714     /** @private */
41715     _handleRefresh : function(url, params, loadOnce){
41716         if(!loadOnce || !this.loaded){
41717             var updater = this.bodyEl.getUpdateManager();
41718             updater.update(url, params, this._setLoaded.createDelegate(this));
41719         }
41720     },
41721
41722     /**
41723      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41724      *   Will fail silently if the setUrl method has not been called.
41725      *   This does not activate the panel, just updates its content.
41726      */
41727     refresh : function(){
41728         if(this.refreshDelegate){
41729            this.loaded = false;
41730            this.refreshDelegate();
41731         }
41732     },
41733
41734     /** @private */
41735     _setLoaded : function(){
41736         this.loaded = true;
41737     },
41738
41739     /** @private */
41740     closeClick : function(e){
41741         var o = {};
41742         e.stopEvent();
41743         this.fireEvent("beforeclose", this, o);
41744         if(o.cancel !== true){
41745             this.tabPanel.removeTab(this.id);
41746         }
41747     },
41748     /**
41749      * The text displayed in the tooltip for the close icon.
41750      * @type String
41751      */
41752     closeText : "Close this tab"
41753 });
41754 /**
41755 *    This script refer to:
41756 *    Title: International Telephone Input
41757 *    Author: Jack O'Connor
41758 *    Code version:  v12.1.12
41759 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41760 **/
41761
41762 Roo.bootstrap.PhoneInputData = function() {
41763     var d = [
41764       [
41765         "Afghanistan (‫افغانستان‬‎)",
41766         "af",
41767         "93"
41768       ],
41769       [
41770         "Albania (Shqipëri)",
41771         "al",
41772         "355"
41773       ],
41774       [
41775         "Algeria (‫الجزائر‬‎)",
41776         "dz",
41777         "213"
41778       ],
41779       [
41780         "American Samoa",
41781         "as",
41782         "1684"
41783       ],
41784       [
41785         "Andorra",
41786         "ad",
41787         "376"
41788       ],
41789       [
41790         "Angola",
41791         "ao",
41792         "244"
41793       ],
41794       [
41795         "Anguilla",
41796         "ai",
41797         "1264"
41798       ],
41799       [
41800         "Antigua and Barbuda",
41801         "ag",
41802         "1268"
41803       ],
41804       [
41805         "Argentina",
41806         "ar",
41807         "54"
41808       ],
41809       [
41810         "Armenia (Հայաստան)",
41811         "am",
41812         "374"
41813       ],
41814       [
41815         "Aruba",
41816         "aw",
41817         "297"
41818       ],
41819       [
41820         "Australia",
41821         "au",
41822         "61",
41823         0
41824       ],
41825       [
41826         "Austria (Österreich)",
41827         "at",
41828         "43"
41829       ],
41830       [
41831         "Azerbaijan (Azərbaycan)",
41832         "az",
41833         "994"
41834       ],
41835       [
41836         "Bahamas",
41837         "bs",
41838         "1242"
41839       ],
41840       [
41841         "Bahrain (‫البحرين‬‎)",
41842         "bh",
41843         "973"
41844       ],
41845       [
41846         "Bangladesh (বাংলাদেশ)",
41847         "bd",
41848         "880"
41849       ],
41850       [
41851         "Barbados",
41852         "bb",
41853         "1246"
41854       ],
41855       [
41856         "Belarus (Беларусь)",
41857         "by",
41858         "375"
41859       ],
41860       [
41861         "Belgium (België)",
41862         "be",
41863         "32"
41864       ],
41865       [
41866         "Belize",
41867         "bz",
41868         "501"
41869       ],
41870       [
41871         "Benin (Bénin)",
41872         "bj",
41873         "229"
41874       ],
41875       [
41876         "Bermuda",
41877         "bm",
41878         "1441"
41879       ],
41880       [
41881         "Bhutan (འབྲུག)",
41882         "bt",
41883         "975"
41884       ],
41885       [
41886         "Bolivia",
41887         "bo",
41888         "591"
41889       ],
41890       [
41891         "Bosnia and Herzegovina (Босна и Херцеговина)",
41892         "ba",
41893         "387"
41894       ],
41895       [
41896         "Botswana",
41897         "bw",
41898         "267"
41899       ],
41900       [
41901         "Brazil (Brasil)",
41902         "br",
41903         "55"
41904       ],
41905       [
41906         "British Indian Ocean Territory",
41907         "io",
41908         "246"
41909       ],
41910       [
41911         "British Virgin Islands",
41912         "vg",
41913         "1284"
41914       ],
41915       [
41916         "Brunei",
41917         "bn",
41918         "673"
41919       ],
41920       [
41921         "Bulgaria (България)",
41922         "bg",
41923         "359"
41924       ],
41925       [
41926         "Burkina Faso",
41927         "bf",
41928         "226"
41929       ],
41930       [
41931         "Burundi (Uburundi)",
41932         "bi",
41933         "257"
41934       ],
41935       [
41936         "Cambodia (កម្ពុជា)",
41937         "kh",
41938         "855"
41939       ],
41940       [
41941         "Cameroon (Cameroun)",
41942         "cm",
41943         "237"
41944       ],
41945       [
41946         "Canada",
41947         "ca",
41948         "1",
41949         1,
41950         ["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"]
41951       ],
41952       [
41953         "Cape Verde (Kabu Verdi)",
41954         "cv",
41955         "238"
41956       ],
41957       [
41958         "Caribbean Netherlands",
41959         "bq",
41960         "599",
41961         1
41962       ],
41963       [
41964         "Cayman Islands",
41965         "ky",
41966         "1345"
41967       ],
41968       [
41969         "Central African Republic (République centrafricaine)",
41970         "cf",
41971         "236"
41972       ],
41973       [
41974         "Chad (Tchad)",
41975         "td",
41976         "235"
41977       ],
41978       [
41979         "Chile",
41980         "cl",
41981         "56"
41982       ],
41983       [
41984         "China (中国)",
41985         "cn",
41986         "86"
41987       ],
41988       [
41989         "Christmas Island",
41990         "cx",
41991         "61",
41992         2
41993       ],
41994       [
41995         "Cocos (Keeling) Islands",
41996         "cc",
41997         "61",
41998         1
41999       ],
42000       [
42001         "Colombia",
42002         "co",
42003         "57"
42004       ],
42005       [
42006         "Comoros (‫جزر القمر‬‎)",
42007         "km",
42008         "269"
42009       ],
42010       [
42011         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42012         "cd",
42013         "243"
42014       ],
42015       [
42016         "Congo (Republic) (Congo-Brazzaville)",
42017         "cg",
42018         "242"
42019       ],
42020       [
42021         "Cook Islands",
42022         "ck",
42023         "682"
42024       ],
42025       [
42026         "Costa Rica",
42027         "cr",
42028         "506"
42029       ],
42030       [
42031         "Côte d’Ivoire",
42032         "ci",
42033         "225"
42034       ],
42035       [
42036         "Croatia (Hrvatska)",
42037         "hr",
42038         "385"
42039       ],
42040       [
42041         "Cuba",
42042         "cu",
42043         "53"
42044       ],
42045       [
42046         "Curaçao",
42047         "cw",
42048         "599",
42049         0
42050       ],
42051       [
42052         "Cyprus (Κύπρος)",
42053         "cy",
42054         "357"
42055       ],
42056       [
42057         "Czech Republic (Česká republika)",
42058         "cz",
42059         "420"
42060       ],
42061       [
42062         "Denmark (Danmark)",
42063         "dk",
42064         "45"
42065       ],
42066       [
42067         "Djibouti",
42068         "dj",
42069         "253"
42070       ],
42071       [
42072         "Dominica",
42073         "dm",
42074         "1767"
42075       ],
42076       [
42077         "Dominican Republic (República Dominicana)",
42078         "do",
42079         "1",
42080         2,
42081         ["809", "829", "849"]
42082       ],
42083       [
42084         "Ecuador",
42085         "ec",
42086         "593"
42087       ],
42088       [
42089         "Egypt (‫مصر‬‎)",
42090         "eg",
42091         "20"
42092       ],
42093       [
42094         "El Salvador",
42095         "sv",
42096         "503"
42097       ],
42098       [
42099         "Equatorial Guinea (Guinea Ecuatorial)",
42100         "gq",
42101         "240"
42102       ],
42103       [
42104         "Eritrea",
42105         "er",
42106         "291"
42107       ],
42108       [
42109         "Estonia (Eesti)",
42110         "ee",
42111         "372"
42112       ],
42113       [
42114         "Ethiopia",
42115         "et",
42116         "251"
42117       ],
42118       [
42119         "Falkland Islands (Islas Malvinas)",
42120         "fk",
42121         "500"
42122       ],
42123       [
42124         "Faroe Islands (Føroyar)",
42125         "fo",
42126         "298"
42127       ],
42128       [
42129         "Fiji",
42130         "fj",
42131         "679"
42132       ],
42133       [
42134         "Finland (Suomi)",
42135         "fi",
42136         "358",
42137         0
42138       ],
42139       [
42140         "France",
42141         "fr",
42142         "33"
42143       ],
42144       [
42145         "French Guiana (Guyane française)",
42146         "gf",
42147         "594"
42148       ],
42149       [
42150         "French Polynesia (Polynésie française)",
42151         "pf",
42152         "689"
42153       ],
42154       [
42155         "Gabon",
42156         "ga",
42157         "241"
42158       ],
42159       [
42160         "Gambia",
42161         "gm",
42162         "220"
42163       ],
42164       [
42165         "Georgia (საქართველო)",
42166         "ge",
42167         "995"
42168       ],
42169       [
42170         "Germany (Deutschland)",
42171         "de",
42172         "49"
42173       ],
42174       [
42175         "Ghana (Gaana)",
42176         "gh",
42177         "233"
42178       ],
42179       [
42180         "Gibraltar",
42181         "gi",
42182         "350"
42183       ],
42184       [
42185         "Greece (Ελλάδα)",
42186         "gr",
42187         "30"
42188       ],
42189       [
42190         "Greenland (Kalaallit Nunaat)",
42191         "gl",
42192         "299"
42193       ],
42194       [
42195         "Grenada",
42196         "gd",
42197         "1473"
42198       ],
42199       [
42200         "Guadeloupe",
42201         "gp",
42202         "590",
42203         0
42204       ],
42205       [
42206         "Guam",
42207         "gu",
42208         "1671"
42209       ],
42210       [
42211         "Guatemala",
42212         "gt",
42213         "502"
42214       ],
42215       [
42216         "Guernsey",
42217         "gg",
42218         "44",
42219         1
42220       ],
42221       [
42222         "Guinea (Guinée)",
42223         "gn",
42224         "224"
42225       ],
42226       [
42227         "Guinea-Bissau (Guiné Bissau)",
42228         "gw",
42229         "245"
42230       ],
42231       [
42232         "Guyana",
42233         "gy",
42234         "592"
42235       ],
42236       [
42237         "Haiti",
42238         "ht",
42239         "509"
42240       ],
42241       [
42242         "Honduras",
42243         "hn",
42244         "504"
42245       ],
42246       [
42247         "Hong Kong (香港)",
42248         "hk",
42249         "852"
42250       ],
42251       [
42252         "Hungary (Magyarország)",
42253         "hu",
42254         "36"
42255       ],
42256       [
42257         "Iceland (Ísland)",
42258         "is",
42259         "354"
42260       ],
42261       [
42262         "India (भारत)",
42263         "in",
42264         "91"
42265       ],
42266       [
42267         "Indonesia",
42268         "id",
42269         "62"
42270       ],
42271       [
42272         "Iran (‫ایران‬‎)",
42273         "ir",
42274         "98"
42275       ],
42276       [
42277         "Iraq (‫العراق‬‎)",
42278         "iq",
42279         "964"
42280       ],
42281       [
42282         "Ireland",
42283         "ie",
42284         "353"
42285       ],
42286       [
42287         "Isle of Man",
42288         "im",
42289         "44",
42290         2
42291       ],
42292       [
42293         "Israel (‫ישראל‬‎)",
42294         "il",
42295         "972"
42296       ],
42297       [
42298         "Italy (Italia)",
42299         "it",
42300         "39",
42301         0
42302       ],
42303       [
42304         "Jamaica",
42305         "jm",
42306         "1876"
42307       ],
42308       [
42309         "Japan (日本)",
42310         "jp",
42311         "81"
42312       ],
42313       [
42314         "Jersey",
42315         "je",
42316         "44",
42317         3
42318       ],
42319       [
42320         "Jordan (‫الأردن‬‎)",
42321         "jo",
42322         "962"
42323       ],
42324       [
42325         "Kazakhstan (Казахстан)",
42326         "kz",
42327         "7",
42328         1
42329       ],
42330       [
42331         "Kenya",
42332         "ke",
42333         "254"
42334       ],
42335       [
42336         "Kiribati",
42337         "ki",
42338         "686"
42339       ],
42340       [
42341         "Kosovo",
42342         "xk",
42343         "383"
42344       ],
42345       [
42346         "Kuwait (‫الكويت‬‎)",
42347         "kw",
42348         "965"
42349       ],
42350       [
42351         "Kyrgyzstan (Кыргызстан)",
42352         "kg",
42353         "996"
42354       ],
42355       [
42356         "Laos (ລາວ)",
42357         "la",
42358         "856"
42359       ],
42360       [
42361         "Latvia (Latvija)",
42362         "lv",
42363         "371"
42364       ],
42365       [
42366         "Lebanon (‫لبنان‬‎)",
42367         "lb",
42368         "961"
42369       ],
42370       [
42371         "Lesotho",
42372         "ls",
42373         "266"
42374       ],
42375       [
42376         "Liberia",
42377         "lr",
42378         "231"
42379       ],
42380       [
42381         "Libya (‫ليبيا‬‎)",
42382         "ly",
42383         "218"
42384       ],
42385       [
42386         "Liechtenstein",
42387         "li",
42388         "423"
42389       ],
42390       [
42391         "Lithuania (Lietuva)",
42392         "lt",
42393         "370"
42394       ],
42395       [
42396         "Luxembourg",
42397         "lu",
42398         "352"
42399       ],
42400       [
42401         "Macau (澳門)",
42402         "mo",
42403         "853"
42404       ],
42405       [
42406         "Macedonia (FYROM) (Македонија)",
42407         "mk",
42408         "389"
42409       ],
42410       [
42411         "Madagascar (Madagasikara)",
42412         "mg",
42413         "261"
42414       ],
42415       [
42416         "Malawi",
42417         "mw",
42418         "265"
42419       ],
42420       [
42421         "Malaysia",
42422         "my",
42423         "60"
42424       ],
42425       [
42426         "Maldives",
42427         "mv",
42428         "960"
42429       ],
42430       [
42431         "Mali",
42432         "ml",
42433         "223"
42434       ],
42435       [
42436         "Malta",
42437         "mt",
42438         "356"
42439       ],
42440       [
42441         "Marshall Islands",
42442         "mh",
42443         "692"
42444       ],
42445       [
42446         "Martinique",
42447         "mq",
42448         "596"
42449       ],
42450       [
42451         "Mauritania (‫موريتانيا‬‎)",
42452         "mr",
42453         "222"
42454       ],
42455       [
42456         "Mauritius (Moris)",
42457         "mu",
42458         "230"
42459       ],
42460       [
42461         "Mayotte",
42462         "yt",
42463         "262",
42464         1
42465       ],
42466       [
42467         "Mexico (México)",
42468         "mx",
42469         "52"
42470       ],
42471       [
42472         "Micronesia",
42473         "fm",
42474         "691"
42475       ],
42476       [
42477         "Moldova (Republica Moldova)",
42478         "md",
42479         "373"
42480       ],
42481       [
42482         "Monaco",
42483         "mc",
42484         "377"
42485       ],
42486       [
42487         "Mongolia (Монгол)",
42488         "mn",
42489         "976"
42490       ],
42491       [
42492         "Montenegro (Crna Gora)",
42493         "me",
42494         "382"
42495       ],
42496       [
42497         "Montserrat",
42498         "ms",
42499         "1664"
42500       ],
42501       [
42502         "Morocco (‫المغرب‬‎)",
42503         "ma",
42504         "212",
42505         0
42506       ],
42507       [
42508         "Mozambique (Moçambique)",
42509         "mz",
42510         "258"
42511       ],
42512       [
42513         "Myanmar (Burma) (မြန်မာ)",
42514         "mm",
42515         "95"
42516       ],
42517       [
42518         "Namibia (Namibië)",
42519         "na",
42520         "264"
42521       ],
42522       [
42523         "Nauru",
42524         "nr",
42525         "674"
42526       ],
42527       [
42528         "Nepal (नेपाल)",
42529         "np",
42530         "977"
42531       ],
42532       [
42533         "Netherlands (Nederland)",
42534         "nl",
42535         "31"
42536       ],
42537       [
42538         "New Caledonia (Nouvelle-Calédonie)",
42539         "nc",
42540         "687"
42541       ],
42542       [
42543         "New Zealand",
42544         "nz",
42545         "64"
42546       ],
42547       [
42548         "Nicaragua",
42549         "ni",
42550         "505"
42551       ],
42552       [
42553         "Niger (Nijar)",
42554         "ne",
42555         "227"
42556       ],
42557       [
42558         "Nigeria",
42559         "ng",
42560         "234"
42561       ],
42562       [
42563         "Niue",
42564         "nu",
42565         "683"
42566       ],
42567       [
42568         "Norfolk Island",
42569         "nf",
42570         "672"
42571       ],
42572       [
42573         "North Korea (조선 민주주의 인민 공화국)",
42574         "kp",
42575         "850"
42576       ],
42577       [
42578         "Northern Mariana Islands",
42579         "mp",
42580         "1670"
42581       ],
42582       [
42583         "Norway (Norge)",
42584         "no",
42585         "47",
42586         0
42587       ],
42588       [
42589         "Oman (‫عُمان‬‎)",
42590         "om",
42591         "968"
42592       ],
42593       [
42594         "Pakistan (‫پاکستان‬‎)",
42595         "pk",
42596         "92"
42597       ],
42598       [
42599         "Palau",
42600         "pw",
42601         "680"
42602       ],
42603       [
42604         "Palestine (‫فلسطين‬‎)",
42605         "ps",
42606         "970"
42607       ],
42608       [
42609         "Panama (Panamá)",
42610         "pa",
42611         "507"
42612       ],
42613       [
42614         "Papua New Guinea",
42615         "pg",
42616         "675"
42617       ],
42618       [
42619         "Paraguay",
42620         "py",
42621         "595"
42622       ],
42623       [
42624         "Peru (Perú)",
42625         "pe",
42626         "51"
42627       ],
42628       [
42629         "Philippines",
42630         "ph",
42631         "63"
42632       ],
42633       [
42634         "Poland (Polska)",
42635         "pl",
42636         "48"
42637       ],
42638       [
42639         "Portugal",
42640         "pt",
42641         "351"
42642       ],
42643       [
42644         "Puerto Rico",
42645         "pr",
42646         "1",
42647         3,
42648         ["787", "939"]
42649       ],
42650       [
42651         "Qatar (‫قطر‬‎)",
42652         "qa",
42653         "974"
42654       ],
42655       [
42656         "Réunion (La Réunion)",
42657         "re",
42658         "262",
42659         0
42660       ],
42661       [
42662         "Romania (România)",
42663         "ro",
42664         "40"
42665       ],
42666       [
42667         "Russia (Россия)",
42668         "ru",
42669         "7",
42670         0
42671       ],
42672       [
42673         "Rwanda",
42674         "rw",
42675         "250"
42676       ],
42677       [
42678         "Saint Barthélemy",
42679         "bl",
42680         "590",
42681         1
42682       ],
42683       [
42684         "Saint Helena",
42685         "sh",
42686         "290"
42687       ],
42688       [
42689         "Saint Kitts and Nevis",
42690         "kn",
42691         "1869"
42692       ],
42693       [
42694         "Saint Lucia",
42695         "lc",
42696         "1758"
42697       ],
42698       [
42699         "Saint Martin (Saint-Martin (partie française))",
42700         "mf",
42701         "590",
42702         2
42703       ],
42704       [
42705         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42706         "pm",
42707         "508"
42708       ],
42709       [
42710         "Saint Vincent and the Grenadines",
42711         "vc",
42712         "1784"
42713       ],
42714       [
42715         "Samoa",
42716         "ws",
42717         "685"
42718       ],
42719       [
42720         "San Marino",
42721         "sm",
42722         "378"
42723       ],
42724       [
42725         "São Tomé and Príncipe (São Tomé e Príncipe)",
42726         "st",
42727         "239"
42728       ],
42729       [
42730         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42731         "sa",
42732         "966"
42733       ],
42734       [
42735         "Senegal (Sénégal)",
42736         "sn",
42737         "221"
42738       ],
42739       [
42740         "Serbia (Србија)",
42741         "rs",
42742         "381"
42743       ],
42744       [
42745         "Seychelles",
42746         "sc",
42747         "248"
42748       ],
42749       [
42750         "Sierra Leone",
42751         "sl",
42752         "232"
42753       ],
42754       [
42755         "Singapore",
42756         "sg",
42757         "65"
42758       ],
42759       [
42760         "Sint Maarten",
42761         "sx",
42762         "1721"
42763       ],
42764       [
42765         "Slovakia (Slovensko)",
42766         "sk",
42767         "421"
42768       ],
42769       [
42770         "Slovenia (Slovenija)",
42771         "si",
42772         "386"
42773       ],
42774       [
42775         "Solomon Islands",
42776         "sb",
42777         "677"
42778       ],
42779       [
42780         "Somalia (Soomaaliya)",
42781         "so",
42782         "252"
42783       ],
42784       [
42785         "South Africa",
42786         "za",
42787         "27"
42788       ],
42789       [
42790         "South Korea (대한민국)",
42791         "kr",
42792         "82"
42793       ],
42794       [
42795         "South Sudan (‫جنوب السودان‬‎)",
42796         "ss",
42797         "211"
42798       ],
42799       [
42800         "Spain (España)",
42801         "es",
42802         "34"
42803       ],
42804       [
42805         "Sri Lanka (ශ්‍රී ලංකාව)",
42806         "lk",
42807         "94"
42808       ],
42809       [
42810         "Sudan (‫السودان‬‎)",
42811         "sd",
42812         "249"
42813       ],
42814       [
42815         "Suriname",
42816         "sr",
42817         "597"
42818       ],
42819       [
42820         "Svalbard and Jan Mayen",
42821         "sj",
42822         "47",
42823         1
42824       ],
42825       [
42826         "Swaziland",
42827         "sz",
42828         "268"
42829       ],
42830       [
42831         "Sweden (Sverige)",
42832         "se",
42833         "46"
42834       ],
42835       [
42836         "Switzerland (Schweiz)",
42837         "ch",
42838         "41"
42839       ],
42840       [
42841         "Syria (‫سوريا‬‎)",
42842         "sy",
42843         "963"
42844       ],
42845       [
42846         "Taiwan (台灣)",
42847         "tw",
42848         "886"
42849       ],
42850       [
42851         "Tajikistan",
42852         "tj",
42853         "992"
42854       ],
42855       [
42856         "Tanzania",
42857         "tz",
42858         "255"
42859       ],
42860       [
42861         "Thailand (ไทย)",
42862         "th",
42863         "66"
42864       ],
42865       [
42866         "Timor-Leste",
42867         "tl",
42868         "670"
42869       ],
42870       [
42871         "Togo",
42872         "tg",
42873         "228"
42874       ],
42875       [
42876         "Tokelau",
42877         "tk",
42878         "690"
42879       ],
42880       [
42881         "Tonga",
42882         "to",
42883         "676"
42884       ],
42885       [
42886         "Trinidad and Tobago",
42887         "tt",
42888         "1868"
42889       ],
42890       [
42891         "Tunisia (‫تونس‬‎)",
42892         "tn",
42893         "216"
42894       ],
42895       [
42896         "Turkey (Türkiye)",
42897         "tr",
42898         "90"
42899       ],
42900       [
42901         "Turkmenistan",
42902         "tm",
42903         "993"
42904       ],
42905       [
42906         "Turks and Caicos Islands",
42907         "tc",
42908         "1649"
42909       ],
42910       [
42911         "Tuvalu",
42912         "tv",
42913         "688"
42914       ],
42915       [
42916         "U.S. Virgin Islands",
42917         "vi",
42918         "1340"
42919       ],
42920       [
42921         "Uganda",
42922         "ug",
42923         "256"
42924       ],
42925       [
42926         "Ukraine (Україна)",
42927         "ua",
42928         "380"
42929       ],
42930       [
42931         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42932         "ae",
42933         "971"
42934       ],
42935       [
42936         "United Kingdom",
42937         "gb",
42938         "44",
42939         0
42940       ],
42941       [
42942         "United States",
42943         "us",
42944         "1",
42945         0
42946       ],
42947       [
42948         "Uruguay",
42949         "uy",
42950         "598"
42951       ],
42952       [
42953         "Uzbekistan (Oʻzbekiston)",
42954         "uz",
42955         "998"
42956       ],
42957       [
42958         "Vanuatu",
42959         "vu",
42960         "678"
42961       ],
42962       [
42963         "Vatican City (Città del Vaticano)",
42964         "va",
42965         "39",
42966         1
42967       ],
42968       [
42969         "Venezuela",
42970         "ve",
42971         "58"
42972       ],
42973       [
42974         "Vietnam (Việt Nam)",
42975         "vn",
42976         "84"
42977       ],
42978       [
42979         "Wallis and Futuna (Wallis-et-Futuna)",
42980         "wf",
42981         "681"
42982       ],
42983       [
42984         "Western Sahara (‫الصحراء الغربية‬‎)",
42985         "eh",
42986         "212",
42987         1
42988       ],
42989       [
42990         "Yemen (‫اليمن‬‎)",
42991         "ye",
42992         "967"
42993       ],
42994       [
42995         "Zambia",
42996         "zm",
42997         "260"
42998       ],
42999       [
43000         "Zimbabwe",
43001         "zw",
43002         "263"
43003       ],
43004       [
43005         "Åland Islands",
43006         "ax",
43007         "358",
43008         1
43009       ]
43010   ];
43011   
43012   return d;
43013 }/**
43014 *    This script refer to:
43015 *    Title: International Telephone Input
43016 *    Author: Jack O'Connor
43017 *    Code version:  v12.1.12
43018 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43019 **/
43020
43021 /**
43022  * @class Roo.bootstrap.PhoneInput
43023  * @extends Roo.bootstrap.TriggerField
43024  * An input with International dial-code selection
43025  
43026  * @cfg {String} defaultDialCode default '+852'
43027  * @cfg {Array} preferedCountries default []
43028   
43029  * @constructor
43030  * Create a new PhoneInput.
43031  * @param {Object} config Configuration options
43032  */
43033
43034 Roo.bootstrap.PhoneInput = function(config) {
43035     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43036 };
43037
43038 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43039         
43040         listWidth: undefined,
43041         
43042         selectedClass: 'active',
43043         
43044         invalidClass : "has-warning",
43045         
43046         validClass: 'has-success',
43047         
43048         allowed: '0123456789',
43049         
43050         max_length: 15,
43051         
43052         /**
43053          * @cfg {String} defaultDialCode The default dial code when initializing the input
43054          */
43055         defaultDialCode: '+852',
43056         
43057         /**
43058          * @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
43059          */
43060         preferedCountries: false,
43061         
43062         getAutoCreate : function()
43063         {
43064             var data = Roo.bootstrap.PhoneInputData();
43065             var align = this.labelAlign || this.parentLabelAlign();
43066             var id = Roo.id();
43067             
43068             this.allCountries = [];
43069             this.dialCodeMapping = [];
43070             
43071             for (var i = 0; i < data.length; i++) {
43072               var c = data[i];
43073               this.allCountries[i] = {
43074                 name: c[0],
43075                 iso2: c[1],
43076                 dialCode: c[2],
43077                 priority: c[3] || 0,
43078                 areaCodes: c[4] || null
43079               };
43080               this.dialCodeMapping[c[2]] = {
43081                   name: c[0],
43082                   iso2: c[1],
43083                   priority: c[3] || 0,
43084                   areaCodes: c[4] || null
43085               };
43086             }
43087             
43088             var cfg = {
43089                 cls: 'form-group',
43090                 cn: []
43091             };
43092             
43093             var input =  {
43094                 tag: 'input',
43095                 id : id,
43096                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43097                 maxlength: this.max_length,
43098                 cls : 'form-control tel-input',
43099                 autocomplete: 'new-password'
43100             };
43101             
43102             var hiddenInput = {
43103                 tag: 'input',
43104                 type: 'hidden',
43105                 cls: 'hidden-tel-input'
43106             };
43107             
43108             if (this.name) {
43109                 hiddenInput.name = this.name;
43110             }
43111             
43112             if (this.disabled) {
43113                 input.disabled = true;
43114             }
43115             
43116             var flag_container = {
43117                 tag: 'div',
43118                 cls: 'flag-box',
43119                 cn: [
43120                     {
43121                         tag: 'div',
43122                         cls: 'flag'
43123                     },
43124                     {
43125                         tag: 'div',
43126                         cls: 'caret'
43127                     }
43128                 ]
43129             };
43130             
43131             var box = {
43132                 tag: 'div',
43133                 cls: this.hasFeedback ? 'has-feedback' : '',
43134                 cn: [
43135                     hiddenInput,
43136                     input,
43137                     {
43138                         tag: 'input',
43139                         cls: 'dial-code-holder',
43140                         disabled: true
43141                     }
43142                 ]
43143             };
43144             
43145             var container = {
43146                 cls: 'roo-select2-container input-group',
43147                 cn: [
43148                     flag_container,
43149                     box
43150                 ]
43151             };
43152             
43153             if (this.fieldLabel.length) {
43154                 var indicator = {
43155                     tag: 'i',
43156                     tooltip: 'This field is required'
43157                 };
43158                 
43159                 var label = {
43160                     tag: 'label',
43161                     'for':  id,
43162                     cls: 'control-label',
43163                     cn: []
43164                 };
43165                 
43166                 var label_text = {
43167                     tag: 'span',
43168                     html: this.fieldLabel
43169                 };
43170                 
43171                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43172                 label.cn = [
43173                     indicator,
43174                     label_text
43175                 ];
43176                 
43177                 if(this.indicatorpos == 'right') {
43178                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43179                     label.cn = [
43180                         label_text,
43181                         indicator
43182                     ];
43183                 }
43184                 
43185                 if(align == 'left') {
43186                     container = {
43187                         tag: 'div',
43188                         cn: [
43189                             container
43190                         ]
43191                     };
43192                     
43193                     if(this.labelWidth > 12){
43194                         label.style = "width: " + this.labelWidth + 'px';
43195                     }
43196                     if(this.labelWidth < 13 && this.labelmd == 0){
43197                         this.labelmd = this.labelWidth;
43198                     }
43199                     if(this.labellg > 0){
43200                         label.cls += ' col-lg-' + this.labellg;
43201                         input.cls += ' col-lg-' + (12 - this.labellg);
43202                     }
43203                     if(this.labelmd > 0){
43204                         label.cls += ' col-md-' + this.labelmd;
43205                         container.cls += ' col-md-' + (12 - this.labelmd);
43206                     }
43207                     if(this.labelsm > 0){
43208                         label.cls += ' col-sm-' + this.labelsm;
43209                         container.cls += ' col-sm-' + (12 - this.labelsm);
43210                     }
43211                     if(this.labelxs > 0){
43212                         label.cls += ' col-xs-' + this.labelxs;
43213                         container.cls += ' col-xs-' + (12 - this.labelxs);
43214                     }
43215                 }
43216             }
43217             
43218             cfg.cn = [
43219                 label,
43220                 container
43221             ];
43222             
43223             var settings = this;
43224             
43225             ['xs','sm','md','lg'].map(function(size){
43226                 if (settings[size]) {
43227                     cfg.cls += ' col-' + size + '-' + settings[size];
43228                 }
43229             });
43230             
43231             this.store = new Roo.data.Store({
43232                 proxy : new Roo.data.MemoryProxy({}),
43233                 reader : new Roo.data.JsonReader({
43234                     fields : [
43235                         {
43236                             'name' : 'name',
43237                             'type' : 'string'
43238                         },
43239                         {
43240                             'name' : 'iso2',
43241                             'type' : 'string'
43242                         },
43243                         {
43244                             'name' : 'dialCode',
43245                             'type' : 'string'
43246                         },
43247                         {
43248                             'name' : 'priority',
43249                             'type' : 'string'
43250                         },
43251                         {
43252                             'name' : 'areaCodes',
43253                             'type' : 'string'
43254                         }
43255                     ]
43256                 })
43257             });
43258             
43259             if(!this.preferedCountries) {
43260                 this.preferedCountries = [
43261                     'hk',
43262                     'gb',
43263                     'us'
43264                 ];
43265             }
43266             
43267             var p = this.preferedCountries.reverse();
43268             
43269             if(p) {
43270                 for (var i = 0; i < p.length; i++) {
43271                     for (var j = 0; j < this.allCountries.length; j++) {
43272                         if(this.allCountries[j].iso2 == p[i]) {
43273                             var t = this.allCountries[j];
43274                             this.allCountries.splice(j,1);
43275                             this.allCountries.unshift(t);
43276                         }
43277                     } 
43278                 }
43279             }
43280             
43281             this.store.proxy.data = {
43282                 success: true,
43283                 data: this.allCountries
43284             };
43285             
43286             return cfg;
43287         },
43288         
43289         initEvents : function()
43290         {
43291             this.createList();
43292             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43293             
43294             this.indicator = this.indicatorEl();
43295             this.flag = this.flagEl();
43296             this.dialCodeHolder = this.dialCodeHolderEl();
43297             
43298             this.trigger = this.el.select('div.flag-box',true).first();
43299             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43300             
43301             var _this = this;
43302             
43303             (function(){
43304                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43305                 _this.list.setWidth(lw);
43306             }).defer(100);
43307             
43308             this.list.on('mouseover', this.onViewOver, this);
43309             this.list.on('mousemove', this.onViewMove, this);
43310             this.inputEl().on("keyup", this.onKeyUp, this);
43311             this.inputEl().on("keypress", this.onKeyPress, this);
43312             
43313             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43314
43315             this.view = new Roo.View(this.list, this.tpl, {
43316                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43317             });
43318             
43319             this.view.on('click', this.onViewClick, this);
43320             this.setValue(this.defaultDialCode);
43321         },
43322         
43323         onTriggerClick : function(e)
43324         {
43325             Roo.log('trigger click');
43326             if(this.disabled){
43327                 return;
43328             }
43329             
43330             if(this.isExpanded()){
43331                 this.collapse();
43332                 this.hasFocus = false;
43333             }else {
43334                 this.store.load({});
43335                 this.hasFocus = true;
43336                 this.expand();
43337             }
43338         },
43339         
43340         isExpanded : function()
43341         {
43342             return this.list.isVisible();
43343         },
43344         
43345         collapse : function()
43346         {
43347             if(!this.isExpanded()){
43348                 return;
43349             }
43350             this.list.hide();
43351             Roo.get(document).un('mousedown', this.collapseIf, this);
43352             Roo.get(document).un('mousewheel', this.collapseIf, this);
43353             this.fireEvent('collapse', this);
43354             this.validate();
43355         },
43356         
43357         expand : function()
43358         {
43359             Roo.log('expand');
43360
43361             if(this.isExpanded() || !this.hasFocus){
43362                 return;
43363             }
43364             
43365             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43366             this.list.setWidth(lw);
43367             
43368             this.list.show();
43369             this.restrictHeight();
43370             
43371             Roo.get(document).on('mousedown', this.collapseIf, this);
43372             Roo.get(document).on('mousewheel', this.collapseIf, this);
43373             
43374             this.fireEvent('expand', this);
43375         },
43376         
43377         restrictHeight : function()
43378         {
43379             this.list.alignTo(this.inputEl(), this.listAlign);
43380             this.list.alignTo(this.inputEl(), this.listAlign);
43381         },
43382         
43383         onViewOver : function(e, t)
43384         {
43385             if(this.inKeyMode){
43386                 return;
43387             }
43388             var item = this.view.findItemFromChild(t);
43389             
43390             if(item){
43391                 var index = this.view.indexOf(item);
43392                 this.select(index, false);
43393             }
43394         },
43395
43396         // private
43397         onViewClick : function(view, doFocus, el, e)
43398         {
43399             var index = this.view.getSelectedIndexes()[0];
43400             
43401             var r = this.store.getAt(index);
43402             
43403             if(r){
43404                 this.onSelect(r, index);
43405             }
43406             if(doFocus !== false && !this.blockFocus){
43407                 this.inputEl().focus();
43408             }
43409         },
43410         
43411         onViewMove : function(e, t)
43412         {
43413             this.inKeyMode = false;
43414         },
43415         
43416         select : function(index, scrollIntoView)
43417         {
43418             this.selectedIndex = index;
43419             this.view.select(index);
43420             if(scrollIntoView !== false){
43421                 var el = this.view.getNode(index);
43422                 if(el){
43423                     this.list.scrollChildIntoView(el, false);
43424                 }
43425             }
43426         },
43427         
43428         createList : function()
43429         {
43430             this.list = Roo.get(document.body).createChild({
43431                 tag: 'ul',
43432                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43433                 style: 'display:none'
43434             });
43435             
43436             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43437         },
43438         
43439         collapseIf : function(e)
43440         {
43441             var in_combo  = e.within(this.el);
43442             var in_list =  e.within(this.list);
43443             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43444             
43445             if (in_combo || in_list || is_list) {
43446                 return;
43447             }
43448             this.collapse();
43449         },
43450         
43451         onSelect : function(record, index)
43452         {
43453             if(this.fireEvent('beforeselect', this, record, index) !== false){
43454                 
43455                 this.setFlagClass(record.data.iso2);
43456                 this.setDialCode(record.data.dialCode);
43457                 this.hasFocus = false;
43458                 this.collapse();
43459                 this.fireEvent('select', this, record, index);
43460             }
43461         },
43462         
43463         flagEl : function()
43464         {
43465             var flag = this.el.select('div.flag',true).first();
43466             if(!flag){
43467                 return false;
43468             }
43469             return flag;
43470         },
43471         
43472         dialCodeHolderEl : function()
43473         {
43474             var d = this.el.select('input.dial-code-holder',true).first();
43475             if(!d){
43476                 return false;
43477             }
43478             return d;
43479         },
43480         
43481         setDialCode : function(v)
43482         {
43483             this.dialCodeHolder.dom.value = '+'+v;
43484         },
43485         
43486         setFlagClass : function(n)
43487         {
43488             this.flag.dom.className = 'flag '+n;
43489         },
43490         
43491         getValue : function()
43492         {
43493             var v = this.inputEl().getValue();
43494             if(this.dialCodeHolder) {
43495                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43496             }
43497             return v;
43498         },
43499         
43500         setValue : function(v)
43501         {
43502             var d = this.getDialCode(v);
43503             
43504             //invalid dial code
43505             if(v.length == 0 || !d || d.length == 0) {
43506                 if(this.rendered){
43507                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43508                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43509                 }
43510                 return;
43511             }
43512             
43513             //valid dial code
43514             this.setFlagClass(this.dialCodeMapping[d].iso2);
43515             this.setDialCode(d);
43516             this.inputEl().dom.value = v.replace('+'+d,'');
43517             this.hiddenEl().dom.value = this.getValue();
43518             
43519             this.validate();
43520         },
43521         
43522         getDialCode : function(v)
43523         {
43524             v = v ||  '';
43525             
43526             if (v.length == 0) {
43527                 return this.dialCodeHolder.dom.value;
43528             }
43529             
43530             var dialCode = "";
43531             if (v.charAt(0) != "+") {
43532                 return false;
43533             }
43534             var numericChars = "";
43535             for (var i = 1; i < v.length; i++) {
43536               var c = v.charAt(i);
43537               if (!isNaN(c)) {
43538                 numericChars += c;
43539                 if (this.dialCodeMapping[numericChars]) {
43540                   dialCode = v.substr(1, i);
43541                 }
43542                 if (numericChars.length == 4) {
43543                   break;
43544                 }
43545               }
43546             }
43547             return dialCode;
43548         },
43549         
43550         reset : function()
43551         {
43552             this.setValue(this.defaultDialCode);
43553             this.validate();
43554         },
43555         
43556         hiddenEl : function()
43557         {
43558             return this.el.select('input.hidden-tel-input',true).first();
43559         },
43560         
43561         // after setting val
43562         onKeyUp : function(e){
43563             this.setValue(this.getValue());
43564         },
43565         
43566         onKeyPress : function(e){
43567             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43568                 e.stopEvent();
43569             }
43570         }
43571         
43572 });
43573 /**
43574  * @class Roo.bootstrap.MoneyField
43575  * @extends Roo.bootstrap.ComboBox
43576  * Bootstrap MoneyField class
43577  * 
43578  * @constructor
43579  * Create a new MoneyField.
43580  * @param {Object} config Configuration options
43581  */
43582
43583 Roo.bootstrap.MoneyField = function(config) {
43584     
43585     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43586     
43587 };
43588
43589 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43590     
43591     /**
43592      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43593      */
43594     allowDecimals : true,
43595     /**
43596      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43597      */
43598     decimalSeparator : ".",
43599     /**
43600      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43601      */
43602     decimalPrecision : 0,
43603     /**
43604      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43605      */
43606     allowNegative : true,
43607     /**
43608      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43609      */
43610     allowZero: true,
43611     /**
43612      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43613      */
43614     minValue : Number.NEGATIVE_INFINITY,
43615     /**
43616      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43617      */
43618     maxValue : Number.MAX_VALUE,
43619     /**
43620      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43621      */
43622     minText : "The minimum value for this field is {0}",
43623     /**
43624      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43625      */
43626     maxText : "The maximum value for this field is {0}",
43627     /**
43628      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43629      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43630      */
43631     nanText : "{0} is not a valid number",
43632     /**
43633      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43634      */
43635     castInt : true,
43636     /**
43637      * @cfg {String} defaults currency of the MoneyField
43638      * value should be in lkey
43639      */
43640     defaultCurrency : false,
43641     /**
43642      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43643      */
43644     thousandsDelimiter : false,
43645     /**
43646      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43647      */
43648     max_length: false,
43649     
43650     inputlg : 9,
43651     inputmd : 9,
43652     inputsm : 9,
43653     inputxs : 6,
43654     
43655     store : false,
43656     
43657     getAutoCreate : function()
43658     {
43659         var align = this.labelAlign || this.parentLabelAlign();
43660         
43661         var id = Roo.id();
43662
43663         var cfg = {
43664             cls: 'form-group',
43665             cn: []
43666         };
43667
43668         var input =  {
43669             tag: 'input',
43670             id : id,
43671             cls : 'form-control roo-money-amount-input',
43672             autocomplete: 'new-password'
43673         };
43674         
43675         var hiddenInput = {
43676             tag: 'input',
43677             type: 'hidden',
43678             id: Roo.id(),
43679             cls: 'hidden-number-input'
43680         };
43681         
43682         if(this.max_length) {
43683             input.maxlength = this.max_length; 
43684         }
43685         
43686         if (this.name) {
43687             hiddenInput.name = this.name;
43688         }
43689
43690         if (this.disabled) {
43691             input.disabled = true;
43692         }
43693
43694         var clg = 12 - this.inputlg;
43695         var cmd = 12 - this.inputmd;
43696         var csm = 12 - this.inputsm;
43697         var cxs = 12 - this.inputxs;
43698         
43699         var container = {
43700             tag : 'div',
43701             cls : 'row roo-money-field',
43702             cn : [
43703                 {
43704                     tag : 'div',
43705                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43706                     cn : [
43707                         {
43708                             tag : 'div',
43709                             cls: 'roo-select2-container input-group',
43710                             cn: [
43711                                 {
43712                                     tag : 'input',
43713                                     cls : 'form-control roo-money-currency-input',
43714                                     autocomplete: 'new-password',
43715                                     readOnly : 1,
43716                                     name : this.currencyName
43717                                 },
43718                                 {
43719                                     tag :'span',
43720                                     cls : 'input-group-addon',
43721                                     cn : [
43722                                         {
43723                                             tag: 'span',
43724                                             cls: 'caret'
43725                                         }
43726                                     ]
43727                                 }
43728                             ]
43729                         }
43730                     ]
43731                 },
43732                 {
43733                     tag : 'div',
43734                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43735                     cn : [
43736                         {
43737                             tag: 'div',
43738                             cls: this.hasFeedback ? 'has-feedback' : '',
43739                             cn: [
43740                                 input
43741                             ]
43742                         }
43743                     ]
43744                 }
43745             ]
43746             
43747         };
43748         
43749         if (this.fieldLabel.length) {
43750             var indicator = {
43751                 tag: 'i',
43752                 tooltip: 'This field is required'
43753             };
43754
43755             var label = {
43756                 tag: 'label',
43757                 'for':  id,
43758                 cls: 'control-label',
43759                 cn: []
43760             };
43761
43762             var label_text = {
43763                 tag: 'span',
43764                 html: this.fieldLabel
43765             };
43766
43767             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43768             label.cn = [
43769                 indicator,
43770                 label_text
43771             ];
43772
43773             if(this.indicatorpos == 'right') {
43774                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43775                 label.cn = [
43776                     label_text,
43777                     indicator
43778                 ];
43779             }
43780
43781             if(align == 'left') {
43782                 container = {
43783                     tag: 'div',
43784                     cn: [
43785                         container
43786                     ]
43787                 };
43788
43789                 if(this.labelWidth > 12){
43790                     label.style = "width: " + this.labelWidth + 'px';
43791                 }
43792                 if(this.labelWidth < 13 && this.labelmd == 0){
43793                     this.labelmd = this.labelWidth;
43794                 }
43795                 if(this.labellg > 0){
43796                     label.cls += ' col-lg-' + this.labellg;
43797                     input.cls += ' col-lg-' + (12 - this.labellg);
43798                 }
43799                 if(this.labelmd > 0){
43800                     label.cls += ' col-md-' + this.labelmd;
43801                     container.cls += ' col-md-' + (12 - this.labelmd);
43802                 }
43803                 if(this.labelsm > 0){
43804                     label.cls += ' col-sm-' + this.labelsm;
43805                     container.cls += ' col-sm-' + (12 - this.labelsm);
43806                 }
43807                 if(this.labelxs > 0){
43808                     label.cls += ' col-xs-' + this.labelxs;
43809                     container.cls += ' col-xs-' + (12 - this.labelxs);
43810                 }
43811             }
43812         }
43813
43814         cfg.cn = [
43815             label,
43816             container,
43817             hiddenInput
43818         ];
43819         
43820         var settings = this;
43821
43822         ['xs','sm','md','lg'].map(function(size){
43823             if (settings[size]) {
43824                 cfg.cls += ' col-' + size + '-' + settings[size];
43825             }
43826         });
43827         
43828         return cfg;
43829     },
43830     
43831     initEvents : function()
43832     {
43833         this.indicator = this.indicatorEl();
43834         
43835         this.initCurrencyEvent();
43836         
43837         this.initNumberEvent();
43838     },
43839     
43840     initCurrencyEvent : function()
43841     {
43842         if (!this.store) {
43843             throw "can not find store for combo";
43844         }
43845         
43846         this.store = Roo.factory(this.store, Roo.data);
43847         this.store.parent = this;
43848         
43849         this.createList();
43850         
43851         this.triggerEl = this.el.select('.input-group-addon', true).first();
43852         
43853         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43854         
43855         var _this = this;
43856         
43857         (function(){
43858             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43859             _this.list.setWidth(lw);
43860         }).defer(100);
43861         
43862         this.list.on('mouseover', this.onViewOver, this);
43863         this.list.on('mousemove', this.onViewMove, this);
43864         this.list.on('scroll', this.onViewScroll, this);
43865         
43866         if(!this.tpl){
43867             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43868         }
43869         
43870         this.view = new Roo.View(this.list, this.tpl, {
43871             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43872         });
43873         
43874         this.view.on('click', this.onViewClick, this);
43875         
43876         this.store.on('beforeload', this.onBeforeLoad, this);
43877         this.store.on('load', this.onLoad, this);
43878         this.store.on('loadexception', this.onLoadException, this);
43879         
43880         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43881             "up" : function(e){
43882                 this.inKeyMode = true;
43883                 this.selectPrev();
43884             },
43885
43886             "down" : function(e){
43887                 if(!this.isExpanded()){
43888                     this.onTriggerClick();
43889                 }else{
43890                     this.inKeyMode = true;
43891                     this.selectNext();
43892                 }
43893             },
43894
43895             "enter" : function(e){
43896                 this.collapse();
43897                 
43898                 if(this.fireEvent("specialkey", this, e)){
43899                     this.onViewClick(false);
43900                 }
43901                 
43902                 return true;
43903             },
43904
43905             "esc" : function(e){
43906                 this.collapse();
43907             },
43908
43909             "tab" : function(e){
43910                 this.collapse();
43911                 
43912                 if(this.fireEvent("specialkey", this, e)){
43913                     this.onViewClick(false);
43914                 }
43915                 
43916                 return true;
43917             },
43918
43919             scope : this,
43920
43921             doRelay : function(foo, bar, hname){
43922                 if(hname == 'down' || this.scope.isExpanded()){
43923                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43924                 }
43925                 return true;
43926             },
43927
43928             forceKeyDown: true
43929         });
43930         
43931         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43932         
43933     },
43934     
43935     initNumberEvent : function(e)
43936     {
43937         this.inputEl().on("keydown" , this.fireKey,  this);
43938         this.inputEl().on("focus", this.onFocus,  this);
43939         this.inputEl().on("blur", this.onBlur,  this);
43940         
43941         this.inputEl().relayEvent('keyup', this);
43942         
43943         if(this.indicator){
43944             this.indicator.addClass('invisible');
43945         }
43946  
43947         this.originalValue = this.getValue();
43948         
43949         if(this.validationEvent == 'keyup'){
43950             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43951             this.inputEl().on('keyup', this.filterValidation, this);
43952         }
43953         else if(this.validationEvent !== false){
43954             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43955         }
43956         
43957         if(this.selectOnFocus){
43958             this.on("focus", this.preFocus, this);
43959             
43960         }
43961         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43962             this.inputEl().on("keypress", this.filterKeys, this);
43963         } else {
43964             this.inputEl().relayEvent('keypress', this);
43965         }
43966         
43967         var allowed = "0123456789";
43968         
43969         if(this.allowDecimals){
43970             allowed += this.decimalSeparator;
43971         }
43972         
43973         if(this.allowNegative){
43974             allowed += "-";
43975         }
43976         
43977         if(this.thousandsDelimiter) {
43978             allowed += ",";
43979         }
43980         
43981         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43982         
43983         var keyPress = function(e){
43984             
43985             var k = e.getKey();
43986             
43987             var c = e.getCharCode();
43988             
43989             if(
43990                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43991                     allowed.indexOf(String.fromCharCode(c)) === -1
43992             ){
43993                 e.stopEvent();
43994                 return;
43995             }
43996             
43997             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43998                 return;
43999             }
44000             
44001             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44002                 e.stopEvent();
44003             }
44004         };
44005         
44006         this.inputEl().on("keypress", keyPress, this);
44007         
44008     },
44009     
44010     onTriggerClick : function(e)
44011     {   
44012         if(this.disabled){
44013             return;
44014         }
44015         
44016         this.page = 0;
44017         this.loadNext = false;
44018         
44019         if(this.isExpanded()){
44020             this.collapse();
44021             return;
44022         }
44023         
44024         this.hasFocus = true;
44025         
44026         if(this.triggerAction == 'all') {
44027             this.doQuery(this.allQuery, true);
44028             return;
44029         }
44030         
44031         this.doQuery(this.getRawValue());
44032     },
44033     
44034     getCurrency : function()
44035     {   
44036         var v = this.currencyEl().getValue();
44037         
44038         return v;
44039     },
44040     
44041     restrictHeight : function()
44042     {
44043         this.list.alignTo(this.currencyEl(), this.listAlign);
44044         this.list.alignTo(this.currencyEl(), this.listAlign);
44045     },
44046     
44047     onViewClick : function(view, doFocus, el, e)
44048     {
44049         var index = this.view.getSelectedIndexes()[0];
44050         
44051         var r = this.store.getAt(index);
44052         
44053         if(r){
44054             this.onSelect(r, index);
44055         }
44056     },
44057     
44058     onSelect : function(record, index){
44059         
44060         if(this.fireEvent('beforeselect', this, record, index) !== false){
44061         
44062             this.setFromCurrencyData(index > -1 ? record.data : false);
44063             
44064             this.collapse();
44065             
44066             this.fireEvent('select', this, record, index);
44067         }
44068     },
44069     
44070     setFromCurrencyData : function(o)
44071     {
44072         var currency = '';
44073         
44074         this.lastCurrency = o;
44075         
44076         if (this.currencyField) {
44077             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44078         } else {
44079             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44080         }
44081         
44082         this.lastSelectionText = currency;
44083         
44084         //setting default currency
44085         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44086             this.setCurrency(this.defaultCurrency);
44087             return;
44088         }
44089         
44090         this.setCurrency(currency);
44091     },
44092     
44093     setFromData : function(o)
44094     {
44095         var c = {};
44096         
44097         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44098         
44099         this.setFromCurrencyData(c);
44100         
44101         var value = '';
44102         
44103         if (this.name) {
44104             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44105         } else {
44106             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44107         }
44108         
44109         this.setValue(value);
44110         
44111     },
44112     
44113     setCurrency : function(v)
44114     {   
44115         this.currencyValue = v;
44116         
44117         if(this.rendered){
44118             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44119             this.validate();
44120         }
44121     },
44122     
44123     setValue : function(v)
44124     {
44125         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44126         
44127         this.value = v;
44128         
44129         if(this.rendered){
44130             
44131             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44132             
44133             this.inputEl().dom.value = (v == '') ? '' :
44134                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44135             
44136             if(!this.allowZero && v === '0') {
44137                 this.hiddenEl().dom.value = '';
44138                 this.inputEl().dom.value = '';
44139             }
44140             
44141             this.validate();
44142         }
44143     },
44144     
44145     getRawValue : function()
44146     {
44147         var v = this.inputEl().getValue();
44148         
44149         return v;
44150     },
44151     
44152     getValue : function()
44153     {
44154         return this.fixPrecision(this.parseValue(this.getRawValue()));
44155     },
44156     
44157     parseValue : function(value)
44158     {
44159         if(this.thousandsDelimiter) {
44160             value += "";
44161             r = new RegExp(",", "g");
44162             value = value.replace(r, "");
44163         }
44164         
44165         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44166         return isNaN(value) ? '' : value;
44167         
44168     },
44169     
44170     fixPrecision : function(value)
44171     {
44172         if(this.thousandsDelimiter) {
44173             value += "";
44174             r = new RegExp(",", "g");
44175             value = value.replace(r, "");
44176         }
44177         
44178         var nan = isNaN(value);
44179         
44180         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44181             return nan ? '' : value;
44182         }
44183         return parseFloat(value).toFixed(this.decimalPrecision);
44184     },
44185     
44186     decimalPrecisionFcn : function(v)
44187     {
44188         return Math.floor(v);
44189     },
44190     
44191     validateValue : function(value)
44192     {
44193         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44194             return false;
44195         }
44196         
44197         var num = this.parseValue(value);
44198         
44199         if(isNaN(num)){
44200             this.markInvalid(String.format(this.nanText, value));
44201             return false;
44202         }
44203         
44204         if(num < this.minValue){
44205             this.markInvalid(String.format(this.minText, this.minValue));
44206             return false;
44207         }
44208         
44209         if(num > this.maxValue){
44210             this.markInvalid(String.format(this.maxText, this.maxValue));
44211             return false;
44212         }
44213         
44214         return true;
44215     },
44216     
44217     validate : function()
44218     {
44219         if(this.disabled || this.allowBlank){
44220             this.markValid();
44221             return true;
44222         }
44223         
44224         var currency = this.getCurrency();
44225         
44226         if(this.validateValue(this.getRawValue()) && currency.length){
44227             this.markValid();
44228             return true;
44229         }
44230         
44231         this.markInvalid();
44232         return false;
44233     },
44234     
44235     getName: function()
44236     {
44237         return this.name;
44238     },
44239     
44240     beforeBlur : function()
44241     {
44242         if(!this.castInt){
44243             return;
44244         }
44245         
44246         var v = this.parseValue(this.getRawValue());
44247         
44248         if(v || v == 0){
44249             this.setValue(v);
44250         }
44251     },
44252     
44253     onBlur : function()
44254     {
44255         this.beforeBlur();
44256         
44257         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44258             //this.el.removeClass(this.focusClass);
44259         }
44260         
44261         this.hasFocus = false;
44262         
44263         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44264             this.validate();
44265         }
44266         
44267         var v = this.getValue();
44268         
44269         if(String(v) !== String(this.startValue)){
44270             this.fireEvent('change', this, v, this.startValue);
44271         }
44272         
44273         this.fireEvent("blur", this);
44274     },
44275     
44276     inputEl : function()
44277     {
44278         return this.el.select('.roo-money-amount-input', true).first();
44279     },
44280     
44281     currencyEl : function()
44282     {
44283         return this.el.select('.roo-money-currency-input', true).first();
44284     },
44285     
44286     hiddenEl : function()
44287     {
44288         return this.el.select('input.hidden-number-input',true).first();
44289     }
44290     
44291 });/**
44292  * @class Roo.bootstrap.BezierSignature
44293  * @extends Roo.bootstrap.Component
44294  * Bootstrap BezierSignature class
44295  * This script refer to:
44296  *    Title: Signature Pad
44297  *    Author: szimek
44298  *    Availability: https://github.com/szimek/signature_pad
44299  *
44300  * @constructor
44301  * Create a new BezierSignature
44302  * @param {Object} config The config object
44303  */
44304
44305 Roo.bootstrap.BezierSignature = function(config){
44306     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44307     this.addEvents({
44308         "resize" : true
44309     });
44310 };
44311
44312 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44313 {
44314      
44315     curve_data: [],
44316     
44317     is_empty: true,
44318     
44319     mouse_btn_down: true,
44320     
44321     /**
44322      * @cfg {int} canvas height
44323      */
44324     canvas_height: '200px',
44325     
44326     /**
44327      * @cfg {float|function} Radius of a single dot.
44328      */ 
44329     dot_size: false,
44330     
44331     /**
44332      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44333      */
44334     min_width: 0.5,
44335     
44336     /**
44337      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44338      */
44339     max_width: 2.5,
44340     
44341     /**
44342      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44343      */
44344     throttle: 16,
44345     
44346     /**
44347      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44348      */
44349     min_distance: 5,
44350     
44351     /**
44352      * @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.
44353      */
44354     bg_color: 'rgba(0, 0, 0, 0)',
44355     
44356     /**
44357      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44358      */
44359     dot_color: 'black',
44360     
44361     /**
44362      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44363      */ 
44364     velocity_filter_weight: 0.7,
44365     
44366     /**
44367      * @cfg {function} Callback when stroke begin. 
44368      */
44369     onBegin: false,
44370     
44371     /**
44372      * @cfg {function} Callback when stroke end.
44373      */
44374     onEnd: false,
44375     
44376     getAutoCreate : function()
44377     {
44378         var cls = 'roo-signature column';
44379         
44380         if(this.cls){
44381             cls += ' ' + this.cls;
44382         }
44383         
44384         var col_sizes = [
44385             'lg',
44386             'md',
44387             'sm',
44388             'xs'
44389         ];
44390         
44391         for(var i = 0; i < col_sizes.length; i++) {
44392             if(this[col_sizes[i]]) {
44393                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44394             }
44395         }
44396         
44397         var cfg = {
44398             tag: 'div',
44399             cls: cls,
44400             cn: [
44401                 {
44402                     tag: 'div',
44403                     cls: 'roo-signature-body',
44404                     cn: [
44405                         {
44406                             tag: 'canvas',
44407                             cls: 'roo-signature-body-canvas',
44408                             height: this.canvas_height,
44409                             width: this.canvas_width
44410                         }
44411                     ]
44412                 },
44413                 {
44414                     tag: 'input',
44415                     type: 'file',
44416                     style: 'display: none'
44417                 }
44418             ]
44419         };
44420         
44421         return cfg;
44422     },
44423     
44424     initEvents: function() 
44425     {
44426         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44427         
44428         var canvas = this.canvasEl();
44429         
44430         // mouse && touch event swapping...
44431         canvas.dom.style.touchAction = 'none';
44432         canvas.dom.style.msTouchAction = 'none';
44433         
44434         this.mouse_btn_down = false;
44435         canvas.on('mousedown', this._handleMouseDown, this);
44436         canvas.on('mousemove', this._handleMouseMove, this);
44437         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44438         
44439         if (window.PointerEvent) {
44440             canvas.on('pointerdown', this._handleMouseDown, this);
44441             canvas.on('pointermove', this._handleMouseMove, this);
44442             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44443         }
44444         
44445         if ('ontouchstart' in window) {
44446             canvas.on('touchstart', this._handleTouchStart, this);
44447             canvas.on('touchmove', this._handleTouchMove, this);
44448             canvas.on('touchend', this._handleTouchEnd, this);
44449         }
44450         
44451         Roo.EventManager.onWindowResize(this.resize, this, true);
44452         
44453         // file input event
44454         this.fileEl().on('change', this.uploadImage, this);
44455         
44456         this.clear();
44457         
44458         this.resize();
44459     },
44460     
44461     resize: function(){
44462         
44463         var canvas = this.canvasEl().dom;
44464         var ctx = this.canvasElCtx();
44465         var img_data = false;
44466         
44467         if(canvas.width > 0) {
44468             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44469         }
44470         // setting canvas width will clean img data
44471         canvas.width = 0;
44472         
44473         var style = window.getComputedStyle ? 
44474             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44475             
44476         var padding_left = parseInt(style.paddingLeft) || 0;
44477         var padding_right = parseInt(style.paddingRight) || 0;
44478         
44479         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44480         
44481         if(img_data) {
44482             ctx.putImageData(img_data, 0, 0);
44483         }
44484     },
44485     
44486     _handleMouseDown: function(e)
44487     {
44488         if (e.browserEvent.which === 1) {
44489             this.mouse_btn_down = true;
44490             this.strokeBegin(e);
44491         }
44492     },
44493     
44494     _handleMouseMove: function (e)
44495     {
44496         if (this.mouse_btn_down) {
44497             this.strokeMoveUpdate(e);
44498         }
44499     },
44500     
44501     _handleMouseUp: function (e)
44502     {
44503         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44504             this.mouse_btn_down = false;
44505             this.strokeEnd(e);
44506         }
44507     },
44508     
44509     _handleTouchStart: function (e) {
44510         
44511         e.preventDefault();
44512         if (e.browserEvent.targetTouches.length === 1) {
44513             // var touch = e.browserEvent.changedTouches[0];
44514             // this.strokeBegin(touch);
44515             
44516              this.strokeBegin(e); // assume e catching the correct xy...
44517         }
44518     },
44519     
44520     _handleTouchMove: function (e) {
44521         e.preventDefault();
44522         // var touch = event.targetTouches[0];
44523         // _this._strokeMoveUpdate(touch);
44524         this.strokeMoveUpdate(e);
44525     },
44526     
44527     _handleTouchEnd: function (e) {
44528         var wasCanvasTouched = e.target === this.canvasEl().dom;
44529         if (wasCanvasTouched) {
44530             e.preventDefault();
44531             // var touch = event.changedTouches[0];
44532             // _this._strokeEnd(touch);
44533             this.strokeEnd(e);
44534         }
44535     },
44536     
44537     reset: function () {
44538         this._lastPoints = [];
44539         this._lastVelocity = 0;
44540         this._lastWidth = (this.min_width + this.max_width) / 2;
44541         this.canvasElCtx().fillStyle = this.dot_color;
44542     },
44543     
44544     strokeMoveUpdate: function(e)
44545     {
44546         this.strokeUpdate(e);
44547         
44548         if (this.throttle) {
44549             this.throttleStroke(this.strokeUpdate, this.throttle);
44550         }
44551         else {
44552             this.strokeUpdate(e);
44553         }
44554     },
44555     
44556     strokeBegin: function(e)
44557     {
44558         var newPointGroup = {
44559             color: this.dot_color,
44560             points: []
44561         };
44562         
44563         if (typeof this.onBegin === 'function') {
44564             this.onBegin(e);
44565         }
44566         
44567         this.curve_data.push(newPointGroup);
44568         this.reset();
44569         this.strokeUpdate(e);
44570     },
44571     
44572     strokeUpdate: function(e)
44573     {
44574         var rect = this.canvasEl().dom.getBoundingClientRect();
44575         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44576         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44577         var lastPoints = lastPointGroup.points;
44578         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44579         var isLastPointTooClose = lastPoint
44580             ? point.distanceTo(lastPoint) <= this.min_distance
44581             : false;
44582         var color = lastPointGroup.color;
44583         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44584             var curve = this.addPoint(point);
44585             if (!lastPoint) {
44586                 this.drawDot({color: color, point: point});
44587             }
44588             else if (curve) {
44589                 this.drawCurve({color: color, curve: curve});
44590             }
44591             lastPoints.push({
44592                 time: point.time,
44593                 x: point.x,
44594                 y: point.y
44595             });
44596         }
44597     },
44598     
44599     strokeEnd: function(e)
44600     {
44601         this.strokeUpdate(e);
44602         if (typeof this.onEnd === 'function') {
44603             this.onEnd(e);
44604         }
44605     },
44606     
44607     addPoint:  function (point) {
44608         var _lastPoints = this._lastPoints;
44609         _lastPoints.push(point);
44610         if (_lastPoints.length > 2) {
44611             if (_lastPoints.length === 3) {
44612                 _lastPoints.unshift(_lastPoints[0]);
44613             }
44614             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44615             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44616             _lastPoints.shift();
44617             return curve;
44618         }
44619         return null;
44620     },
44621     
44622     calculateCurveWidths: function (startPoint, endPoint) {
44623         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44624             (1 - this.velocity_filter_weight) * this._lastVelocity;
44625
44626         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44627         var widths = {
44628             end: newWidth,
44629             start: this._lastWidth
44630         };
44631         
44632         this._lastVelocity = velocity;
44633         this._lastWidth = newWidth;
44634         return widths;
44635     },
44636     
44637     drawDot: function (_a) {
44638         var color = _a.color, point = _a.point;
44639         var ctx = this.canvasElCtx();
44640         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44641         ctx.beginPath();
44642         this.drawCurveSegment(point.x, point.y, width);
44643         ctx.closePath();
44644         ctx.fillStyle = color;
44645         ctx.fill();
44646     },
44647     
44648     drawCurve: function (_a) {
44649         var color = _a.color, curve = _a.curve;
44650         var ctx = this.canvasElCtx();
44651         var widthDelta = curve.endWidth - curve.startWidth;
44652         var drawSteps = Math.floor(curve.length()) * 2;
44653         ctx.beginPath();
44654         ctx.fillStyle = color;
44655         for (var i = 0; i < drawSteps; i += 1) {
44656         var t = i / drawSteps;
44657         var tt = t * t;
44658         var ttt = tt * t;
44659         var u = 1 - t;
44660         var uu = u * u;
44661         var uuu = uu * u;
44662         var x = uuu * curve.startPoint.x;
44663         x += 3 * uu * t * curve.control1.x;
44664         x += 3 * u * tt * curve.control2.x;
44665         x += ttt * curve.endPoint.x;
44666         var y = uuu * curve.startPoint.y;
44667         y += 3 * uu * t * curve.control1.y;
44668         y += 3 * u * tt * curve.control2.y;
44669         y += ttt * curve.endPoint.y;
44670         var width = curve.startWidth + ttt * widthDelta;
44671         this.drawCurveSegment(x, y, width);
44672         }
44673         ctx.closePath();
44674         ctx.fill();
44675     },
44676     
44677     drawCurveSegment: function (x, y, width) {
44678         var ctx = this.canvasElCtx();
44679         ctx.moveTo(x, y);
44680         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44681         this.is_empty = false;
44682     },
44683     
44684     clear: function()
44685     {
44686         var ctx = this.canvasElCtx();
44687         var canvas = this.canvasEl().dom;
44688         ctx.fillStyle = this.bg_color;
44689         ctx.clearRect(0, 0, canvas.width, canvas.height);
44690         ctx.fillRect(0, 0, canvas.width, canvas.height);
44691         this.curve_data = [];
44692         this.reset();
44693         this.is_empty = true;
44694     },
44695     
44696     fileEl: function()
44697     {
44698         return  this.el.select('input',true).first();
44699     },
44700     
44701     canvasEl: function()
44702     {
44703         return this.el.select('canvas',true).first();
44704     },
44705     
44706     canvasElCtx: function()
44707     {
44708         return this.el.select('canvas',true).first().dom.getContext('2d');
44709     },
44710     
44711     getImage: function(type)
44712     {
44713         if(this.is_empty) {
44714             return false;
44715         }
44716         
44717         // encryption ?
44718         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44719     },
44720     
44721     drawFromImage: function(img_src)
44722     {
44723         var img = new Image();
44724         
44725         img.onload = function(){
44726             this.canvasElCtx().drawImage(img, 0, 0);
44727         }.bind(this);
44728         
44729         img.src = img_src;
44730         
44731         this.is_empty = false;
44732     },
44733     
44734     selectImage: function()
44735     {
44736         this.fileEl().dom.click();
44737     },
44738     
44739     uploadImage: function(e)
44740     {
44741         var reader = new FileReader();
44742         
44743         reader.onload = function(e){
44744             var img = new Image();
44745             img.onload = function(){
44746                 this.reset();
44747                 this.canvasElCtx().drawImage(img, 0, 0);
44748             }.bind(this);
44749             img.src = e.target.result;
44750         }.bind(this);
44751         
44752         reader.readAsDataURL(e.target.files[0]);
44753     },
44754     
44755     // Bezier Point Constructor
44756     Point: (function () {
44757         function Point(x, y, time) {
44758             this.x = x;
44759             this.y = y;
44760             this.time = time || Date.now();
44761         }
44762         Point.prototype.distanceTo = function (start) {
44763             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44764         };
44765         Point.prototype.equals = function (other) {
44766             return this.x === other.x && this.y === other.y && this.time === other.time;
44767         };
44768         Point.prototype.velocityFrom = function (start) {
44769             return this.time !== start.time
44770             ? this.distanceTo(start) / (this.time - start.time)
44771             : 0;
44772         };
44773         return Point;
44774     }()),
44775     
44776     
44777     // Bezier Constructor
44778     Bezier: (function () {
44779         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44780             this.startPoint = startPoint;
44781             this.control2 = control2;
44782             this.control1 = control1;
44783             this.endPoint = endPoint;
44784             this.startWidth = startWidth;
44785             this.endWidth = endWidth;
44786         }
44787         Bezier.fromPoints = function (points, widths, scope) {
44788             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44789             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44790             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44791         };
44792         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44793             var dx1 = s1.x - s2.x;
44794             var dy1 = s1.y - s2.y;
44795             var dx2 = s2.x - s3.x;
44796             var dy2 = s2.y - s3.y;
44797             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44798             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44799             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44800             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44801             var dxm = m1.x - m2.x;
44802             var dym = m1.y - m2.y;
44803             var k = l2 / (l1 + l2);
44804             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44805             var tx = s2.x - cm.x;
44806             var ty = s2.y - cm.y;
44807             return {
44808                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44809                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44810             };
44811         };
44812         Bezier.prototype.length = function () {
44813             var steps = 10;
44814             var length = 0;
44815             var px;
44816             var py;
44817             for (var i = 0; i <= steps; i += 1) {
44818                 var t = i / steps;
44819                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44820                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44821                 if (i > 0) {
44822                     var xdiff = cx - px;
44823                     var ydiff = cy - py;
44824                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44825                 }
44826                 px = cx;
44827                 py = cy;
44828             }
44829             return length;
44830         };
44831         Bezier.prototype.point = function (t, start, c1, c2, end) {
44832             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44833             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44834             + (3.0 * c2 * (1.0 - t) * t * t)
44835             + (end * t * t * t);
44836         };
44837         return Bezier;
44838     }()),
44839     
44840     throttleStroke: function(fn, wait) {
44841       if (wait === void 0) { wait = 250; }
44842       var previous = 0;
44843       var timeout = null;
44844       var result;
44845       var storedContext;
44846       var storedArgs;
44847       var later = function () {
44848           previous = Date.now();
44849           timeout = null;
44850           result = fn.apply(storedContext, storedArgs);
44851           if (!timeout) {
44852               storedContext = null;
44853               storedArgs = [];
44854           }
44855       };
44856       return function wrapper() {
44857           var args = [];
44858           for (var _i = 0; _i < arguments.length; _i++) {
44859               args[_i] = arguments[_i];
44860           }
44861           var now = Date.now();
44862           var remaining = wait - (now - previous);
44863           storedContext = this;
44864           storedArgs = args;
44865           if (remaining <= 0 || remaining > wait) {
44866               if (timeout) {
44867                   clearTimeout(timeout);
44868                   timeout = null;
44869               }
44870               previous = now;
44871               result = fn.apply(storedContext, storedArgs);
44872               if (!timeout) {
44873                   storedContext = null;
44874                   storedArgs = [];
44875               }
44876           }
44877           else if (!timeout) {
44878               timeout = window.setTimeout(later, remaining);
44879           }
44880           return result;
44881       };
44882   }
44883   
44884 });
44885
44886  
44887
44888